Apa perbedaan antara global (), lokal (), dan vars ()?

146

Apa perbedaan antara globals(), locals()dan vars()? Apa yang mereka kembalikan? Apakah pembaruan untuk hasil bermanfaat?

Ethan Furman
sumber
dapatkah seseorang memperbarui semua ini dengan python 3 dan membuat skrip benar-benar berfungsi?
Charlie Parker

Jawaban:

172

Masing-masing mengembalikan kamus:

  • globals() selalu mengembalikan kamus namespace modul
  • locals() selalu mengembalikan sebuah kamus dari saat namespace
  • vars()kembali baik kamus namespace saat ini (jika disebut dengan tidak ada argumen) atau yang kamus argumen.

localsdan varsbisa menggunakan penjelasan lebih lanjut. Jika locals()dipanggil di dalam suatu fungsi, ia akan memperbarui dict dengan nilai-nilai namespace variabel lokal saat ini (ditambah variabel penutupan apa pun) pada saat itu dan mengembalikannya. Beberapa panggilan ke locals()dalam bingkai tumpukan yang sama mengembalikan dict yang sama setiap kali - itu dilampirkan ke objek bingkai tumpukan sebagai f_localsatributnya. Konten dikt diperbarui pada setiap locals()panggilan dan setiap f_localsakses atribut, tetapi hanya pada panggilan atau akses atribut tersebut. Itu tidak secara otomatis memperbarui ketika variabel ditugaskan, dan menugaskan entri dalam dikt tidak akan menetapkan variabel lokal yang sesuai:

import inspect

def f():
    x = 1
    l = locals()
    print(l)
    locals()
    print(l)
    x = 2
    print(x, l['x'])
    l['x'] = 3
    print(x, l['x'])
    inspect.currentframe().f_locals
    print(x, l['x'])

f()

memberi kita:

{'x': 1}
{'x': 1, 'l': {...}}
2 1
2 3
2 2

Yang pertama print(l)hanya menunjukkan 'x'entri, karena penugasan lterjadi setelah locals()panggilan. Yang kedua print(l), setelah menelepon locals()lagi, menunjukkan lentri, meskipun kami tidak menyimpan nilai pengembalian. Yang ketiga dan keempat printmenunjukkan bahwa menugaskan variabel tidak memperbarui ldan sebaliknya, tetapi setelah kita mengakses f_locals, variabel lokal disalin locals()lagi.

Dua catatan:

  1. Perilaku ini khusus untuk CPython - Python lain mungkin mengizinkan pembaruan untuk membuatnya kembali ke namespace lokal secara otomatis.
  2. Dalam CPython 2.x dimungkinkan untuk membuat pekerjaan ini dengan meletakkan exec "pass"garis di fungsi. Ini mengalihkan fungsi ke mode eksekusi yang lebih lama dan lebih lambat yang menggunakan locals()dict sebagai representasi kanonik variabel lokal.

Jika locals()dipanggil di luar fungsi, ia mengembalikan kamus aktual yang merupakan namespace saat ini. Perubahan selanjutnya ke namespace yang tercermin dalam kamus, dan perubahan kamus yang tercermin dalam namespace:

class Test(object):
    a = 'one'
    b = 'two'
    huh = locals()
    c = 'three'
    huh['d'] = 'four'
    print huh

memberi kita:

{
  'a': 'one',
  'b': 'two',
  'c': 'three',
  'd': 'four',
  'huh': {...},
  '__module__': '__main__',
}

Sejauh ini, semua yang saya katakan tentang locals()itu juga berlaku untuk vars()... inilah bedanya: vars()menerima satu objek sebagai argumennya, dan jika Anda memberikannya objek, ia mengembalikan __dict__objek itu. Untuk objek tipikal, ini __dict__adalah tempat sebagian besar data atributnya disimpan. Ini termasuk variabel kelas dan modul global:

class Test(object):
    a = 'one'
    b = 'two'
    def frobber(self):
        print self.c
t = Test()
huh = vars(t)
huh['c'] = 'three'
t.frobber()

yang memberi kita:

three

Perhatikan bahwa fungsi __dict__adalah namespace atributnya, bukan variabel lokal. Tidak masuk akal jika suatu fungsi __dict__menyimpan variabel lokal, karena rekursi dan multithreading berarti ada beberapa panggilan ke suatu fungsi pada saat bersamaan, masing-masing dengan penduduk lokal mereka sendiri:

def f(outer):
    if outer:
        f(False)
        print('Outer call locals:', locals())
        print('f.__dict__:', f.__dict__)
    else:
        print('Inner call locals:', locals())
        print('f.__dict__:', f.__dict__)

f.x = 3

f(True)

yang memberi kita:

Inner call locals: {'outer': False}
f.__dict__: {'x': 3}
Outer call locals: {'outer': True}
f.__dict__: {'x': 3}

Di sini, fpanggilan itu sendiri secara rekursif, sehingga panggilan dalam dan luar tumpang tindih. Masing-masing melihat variabel lokal sendiri ketika panggilan locals(), tetapi kedua panggilan melihat hal yang sama f.__dict__, dan f.__dict__tidak memiliki variabel lokal di dalamnya.

Ethan Furman
sumber
4
Bagian "dan setiap tugas ke kamus tidak tercermin dalam namespace lokal yang sebenarnya" mungkin akan sedikit diucapkan .
Sven Marnach
Anehnya, Anda dapat mengakses variabel yang ditambahkan ke vars()atau locals()kamus yang disebut dalam suatu fungsi jika Anda menggunakannya eval(). EG: def test(): huh = locals(); huh['d'] = 4; print eval('d')mencetak 4 saat test()dieksekusi!
Mark Mikofski
1
Sebenarnya penugasan ke dict(dikembalikan oleh locals()) kebetulan tercermin di namespace lokal dan perubahan ke namespace lokal kebetulan tercermin dalam dict(dalam python saya). Satu-satunya hal adalah spesifikasi tidak menjamin perilaku ini.
menjulang tinggi
Penggunaan istilah lingkup nama terlihat lebih mudah bagi saya daripada namespace .
overexchange
1
@overexchange: import thisdan di googlesite:docs.python.org namespace
Ethan Furman