Deskripsi singkat tentang aturan pelingkupan?

472

Apa sebenarnya aturan pelingkupan Python?

Jika saya memiliki beberapa kode:

code1
class Foo:
   code2
   def spam.....
      code3
      for code4..:
       code5
       x()

Dimana xditemukan? Beberapa pilihan yang mungkin termasuk daftar di bawah ini:

  1. Dalam file sumber terlampir
  2. Di namespace kelas
  3. Dalam definisi fungsi
  4. Dalam variabel untuk loop indeks
  5. Di dalam for loop

Juga ada konteks selama eksekusi, ketika fungsi spamdilewatkan di tempat lain. Dan mungkin fungsi lambda melewati sedikit berbeda?

Harus ada referensi atau algoritma sederhana di suatu tempat. Ini adalah dunia yang membingungkan bagi programmer Python perantara.

Charles Merriam
sumber
2
Aturan pelingkupan dijelaskan cukup singkat - tetapi juga sepenuhnya - dalam dokumentasi Python: docs.python.org/3/reference/… .
jefe2000

Jawaban:

420

Sebenarnya, aturan ringkas untuk resolusi Python Scope, dari Learning Python, 3rd. Ed. . (Aturan ini khusus untuk nama variabel, bukan atribut. Jika Anda mereferensikan tanpa titik, aturan ini berlaku.)

Aturan LEGB

  • L ocal - Nama yang ditetapkan dengan cara apa pun dalam suatu fungsi ( defatau lambda), dan tidak dinyatakan global dalam fungsi itu

  • E -fungsi penutup - Nama yang ditetapkan dalam lingkup lokal dari setiap dan semua fungsi penutup statis ( defatau lambda), dari dalam ke luar

  • G lobal (module) - Nama yang ditetapkan di tingkat atas file modul, atau dengan menjalankan globalpernyataan di defdalam file

  • B uilt-in (Python) - Nama preassigned di built-in modul nama: open, range, SyntaxError, dll

Jadi, dalam kasus

code1
class Foo:
    code2
    def spam():
        code3
        for code4:
            code5
            x()

The forLoop tidak memiliki ruang nama sendiri. Dalam urutan LEGB, cakupannya adalah

  • L: di lokal def spam(di code3, code4, dan code5)
  • E: Setiap fungsi penutup (jika seluruh contoh berada di yang lain def)
  • G: Apakah ada yang xdinyatakan secara global dalam modul (dalam code1)?
  • B: Setiap builtin xdengan Python.

xtidak akan pernah ditemukan di code2(bahkan dalam kasus di mana Anda mungkin mengharapkannya, lihat jawaban Antti atau di sini ).

Rizwan Kassim
sumber
45
Sebagai peringatan untuk akses Global - membaca variabel global dapat terjadi tanpa deklarasi eksplisit, tetapi menulis padanya tanpa mendeklarasikan global (var_name) akan membuat instance lokal baru.
Peter Gibson
12
Sebenarnya @Peter, global(var_name)secara sintaksis salah. Sintaks yang benar adalah global var_nametanpa tanda kurung. Anda memiliki poin yang valid.
martineau
Jika demikian, maka mengapa variabel "y" foo tidak terlihat oleh "bar" di bawah: >>> def foo(x): ... y = x ... def bar(z): ... y = z ... bar(5) ... print x,y ... >>> foo(3) 3 3
Jonathan Mayer
3
@ Jonathan: Karena masing y- masing sedang ditulis dan tidak ada global ydeklarasi - lihat komentar @ Peter.
martineau
@LakshmanPrasad Ini jatuh ke "E", tetapi memiliki satu perilaku khusus yang layak disebutkan: itu adalah variabel kelas, jadi itu adalah "global" di antara objek itu. Menugaskannya akan menyebabkan masalah yang tidak terduga dan sulit untuk debug jika Anda tidak tahu apa yang Anda lakukan.
Ctrl-C
157

Pada dasarnya, satu-satunya hal di Python yang memperkenalkan ruang lingkup baru adalah definisi fungsi. Kelas adalah sedikit kasus khusus di mana segala sesuatu yang didefinisikan langsung di tubuh ditempatkan di namespace kelas, tetapi mereka tidak dapat diakses secara langsung dari dalam metode (atau kelas bersarang) yang dikandungnya.

Dalam contoh Anda, hanya ada 3 cakupan di mana x akan dicari:

  • ruang lingkup spam - berisi semua yang didefinisikan dalam code3 dan code5 (serta code4, variabel loop Anda)

  • Cakupan global - berisi semua yang didefinisikan dalam kode1, serta Foo (dan perubahan apa pun setelahnya)

  • Namespace builtin. Sedikit kasus khusus - ini berisi berbagai fungsi dan tipe Python builtin seperti len () dan str (). Biasanya ini tidak boleh dimodifikasi oleh kode pengguna apa pun, jadi harap ini berisi fungsi standar dan tidak ada yang lain.

Lebih banyak cakupan hanya muncul ketika Anda memperkenalkan fungsi bersarang (atau lambda) ke dalam gambar. Ini akan berperilaku cukup seperti yang Anda harapkan. Fungsi bersarang dapat mengakses segala sesuatu dalam lingkup lokal, serta apa pun dalam lingkup fungsi terlampir. misalnya.

def foo():
    x=4
    def bar():
        print x  # Accesses x from foo's scope
    bar()  # Prints 4
    x=5
    bar()  # Prints 5

Pembatasan:

Variabel dalam lingkup selain variabel fungsi lokal dapat diakses, tetapi tidak dapat dikembalikan ke parameter baru tanpa sintaksis lebih lanjut. Sebagai gantinya, penugasan akan membuat variabel lokal baru alih-alih memengaruhi variabel dalam lingkup induk. Sebagai contoh:

global_var1 = []
global_var2 = 1

def func():
    # This is OK: It's just accessing, not rebinding
    global_var1.append(4) 

    # This won't affect global_var2. Instead it creates a new variable
    global_var2 = 2 

    local1 = 4
    def embedded_func():
        # Again, this doen't affect func's local1 variable.  It creates a 
        # new local variable also called local1 instead.
        local1 = 5
        print local1

    embedded_func() # Prints 5
    print local1    # Prints 4

Untuk benar-benar memodifikasi ikatan variabel global dari dalam lingkup fungsi, Anda perlu menentukan bahwa variabel tersebut bersifat global dengan kata kunci global. Misalnya:

global_var = 4
def change_global():
    global global_var
    global_var = global_var + 1

Saat ini tidak ada cara untuk melakukan hal yang sama untuk variabel dalam melampirkan lingkup fungsi , tetapi Python 3 memperkenalkan kata kunci baru, " nonlocal" yang akan bertindak dalam cara yang mirip dengan global, tetapi untuk lingkup fungsi bersarang.

Brian
sumber
111

Tidak ada jawaban menyeluruh tentang waktu Python3, jadi saya membuat jawaban di sini. Sebagian besar yang dijelaskan di sini dirinci dalam Resolusi 4.2.2 nama - nama dokumentasi Python 3.

Seperti yang diberikan dalam jawaban lain, ada 4 cakupan dasar, LEGB, untuk Lokal, Melampirkan, Global dan Builtin. Selain itu, ada ruang lingkup khusus, badan kelas , yang tidak mencakup ruang lingkup terlampir untuk metode yang didefinisikan dalam kelas; tugas apa pun dalam tubuh kelas membuat variabel dari sana terikat di tubuh kelas.

Terutama, tidak ada pernyataan blokir, selain defdan class, membuat ruang lingkup variabel. Dalam Python 2 daftar pemahaman tidak membuat lingkup variabel, namun dalam Python 3 variabel lingkaran dalam daftar daftar dibuat dalam lingkup baru.

Untuk menunjukkan kekhasan badan kelas

x = 0
class X(object):
    y = x
    x = x + 1 # x is now a variable
    z = x

    def method(self):
        print(self.x) # -> 1
        print(x)      # -> 0, the global x
        print(y)      # -> NameError: global name 'y' is not defined

inst = X()
print(inst.x, inst.y, inst.z, x) # -> (1, 0, 1, 0)

Jadi tidak seperti dalam fungsi body, Anda dapat menetapkan kembali variabel ke nama yang sama di kelas tubuh, untuk mendapatkan variabel kelas dengan nama yang sama; pencarian lebih lanjut pada nama ini diselesaikan ke variabel kelas sebagai gantinya.


Salah satu kejutan yang lebih besar bagi banyak pendatang baru di Python adalah forloop tidak membuat lingkup variabel. Dalam Python 2, daftar pemahaman tidak membuat ruang lingkup baik (sementara generator dan pemahaman dikte lakukan!) Alih-alih mereka membocorkan nilai dalam fungsi atau lingkup global:

>>> [ i for i in range(5) ]
>>> i
4

Pemahaman dapat digunakan sebagai cara yang licik (atau mengerikan jika Anda mau) untuk membuat variabel yang dapat dimodifikasi dalam ekspresi lambda di Python 2 - ekspresi lambda memang menciptakan ruang lingkup variabel, seperti defpernyataan itu, tetapi dalam lambda tidak ada pernyataan yang diizinkan. Penugasan sebagai pernyataan dalam Python berarti bahwa tidak ada penugasan variabel dalam lambda yang diizinkan, tetapi pemahaman daftar adalah ekspresi ...

Perilaku ini telah diperbaiki dalam Python 3 - tidak ada ekspresi pemahaman atau variabel generator bocor.


Global benar-benar berarti ruang lingkup modul; modul python utama adalah __main__; semua modul yang diimpor dapat diakses melalui sys.modulesvariabel; untuk mendapatkan akses ke __main__satu dapat menggunakan sys.modules['__main__'], atau import __main__; sangat dapat diterima untuk mengakses dan menetapkan atribut di sana; mereka akan ditampilkan sebagai variabel dalam lingkup global modul utama.


Jika suatu nama pernah ditugaskan ke dalam lingkup saat ini (kecuali dalam lingkup kelas), itu akan dianggap milik lingkup itu, jika tidak maka akan dianggap milik setiap lingkup terlampir yang ditugaskan ke variabel (itu mungkin tidak ditugaskan belum, atau tidak sama sekali), atau akhirnya ruang lingkup global. Jika variabel dianggap lokal, tetapi belum disetel, atau telah dihapus, membaca nilai variabel akan menghasilkan UnboundLocalError, yang merupakan subkelas dari NameError.

x = 5
def foobar():
    print(x)  # causes UnboundLocalError!
    x += 1    # because assignment here makes x a local variable within the function

# call the function
foobar()

Lingkup dapat menyatakan bahwa ia secara eksplisit ingin memodifikasi variabel global (cakupan modul), dengan kata kunci global:

x = 5
def foobar():
    global x
    print(x)
    x += 1

foobar() # -> 5
print(x) # -> 6

Ini juga dimungkinkan bahkan jika dibayangi dalam cakupan terlampir:

x = 5
y = 13
def make_closure():
    x = 42
    y = 911
    def func():
        global x # sees the global value
        print(x, y)
        x += 1

    return func

func = make_closure()
func()      # -> 5 911
print(x, y) # -> 6 13

Dalam python 2 tidak ada cara mudah untuk mengubah nilai dalam lingkup terlampir; biasanya ini disimulasikan dengan memiliki nilai yang bisa berubah, seperti daftar dengan panjang 1:

def make_closure():
    value = [0]
    def get_next_value():
        value[0] += 1
        return value[0]

    return get_next_value

get_next = make_closure()
print(get_next()) # -> 1
print(get_next()) # -> 2

Namun dalam python 3, nonlocaldatang untuk menyelamatkan:

def make_closure():
    value = 0
    def get_next_value():
        nonlocal value
        value += 1
        return value
    return get_next_value

get_next = make_closure() # identical behavior to the previous example.

The nonlocaldokumentasi mengatakan bahwa

Nama-nama yang tercantum dalam pernyataan nonlokal, tidak seperti yang tercantum dalam pernyataan global, harus merujuk ke binding yang sudah ada sebelumnya dalam lingkup terlampir (lingkup di mana ikatan baru harus dibuat tidak dapat ditentukan secara jelas).

yaitu nonlocalselalu mengacu pada ruang lingkup non-global terluar terdalam di mana nama telah diikat (yaitu ditugaskan untuk, termasuk digunakan sebagai forvariabel target, dalam withklausa, atau sebagai parameter fungsi).


Setiap variabel yang tidak dianggap lokal ke lingkup saat ini, atau ruang lingkup apa pun, adalah variabel global. Nama global dilihat dalam kamus global modul; jika tidak ditemukan, global kemudian dilihat dari modul builtins; nama modul diubah dari python 2 menjadi python 3; di python 2 dulu __builtin__dan di python 3 sekarang disebut builtins. Jika Anda menetapkan atribut modul builtin, modul tersebut akan terlihat setelahnya ke modul apa pun sebagai variabel global yang dapat dibaca, kecuali modul itu membayangi mereka dengan variabel globalnya sendiri dengan nama yang sama.


Membaca modul builtin juga bisa bermanfaat; misalkan Anda menginginkan fungsi cetak gaya python 3 di beberapa bagian file, tetapi bagian lain dari file masih menggunakan printpernyataan. Di Python 2.6-2.7 Anda bisa mendapatkan printfungsi Python 3 dengan:

import __builtin__

print3 = __builtin__.__dict__['print']

The from __future__ import print_functionsebenarnya tidak mengimpor printfungsi di mana saja di Python 2 - bukan hanya menonaktifkan aturan parsing untuk printpernyataan dalam modul saat ini, penanganan printseperti variabel pengenal lainnya, dan dengan demikian memungkinkan printfungsi mendongak di builtin.

Antti Haapala
sumber
23

Aturan pelingkupan untuk Python 2.x telah diuraikan di jawaban lain. Satu-satunya hal yang akan saya tambahkan adalah bahwa dalam Python 3.0, ada juga konsep lingkup non-lokal (ditunjukkan oleh kata kunci 'nonlokal'). Hal ini memungkinkan Anda untuk mengakses lingkup luar secara langsung, dan membuka kemampuan untuk melakukan beberapa trik yang rapi, termasuk penutupan leksikal (tanpa peretasan jelek yang melibatkan objek yang dapat diubah).

EDIT: Inilah PEP dengan informasi lebih lanjut tentang ini.

Jeremy Cantrell
sumber
23

Contoh cakupan yang sedikit lebih lengkap:

from __future__ import print_function  # for python 2 support

x = 100
print("1. Global x:", x)
class Test(object):
    y = x
    print("2. Enclosed y:", y)
    x = x + 1
    print("3. Enclosed x:", x)

    def method(self):
        print("4. Enclosed self.x", self.x)
        print("5. Global x", x)
        try:
            print(y)
        except NameError as e:
            print("6.", e)

    def method_local_ref(self):
        try:
            print(x)
        except UnboundLocalError as e:
            print("7.", e)
        x = 200 # causing 7 because has same name
        print("8. Local x", x)

inst = Test()
inst.method()
inst.method_local_ref()

keluaran:

1. Global x: 100
2. Enclosed y: 100
3. Enclosed x: 101
4. Enclosed self.x 101
5. Global x 100
6. global name 'y' is not defined
7. local variable 'x' referenced before assignment
8. Local x 200
brianray
sumber
6
Ini jawaban yang bagus. Namun, saya berpikir bahwa perbedaan antara methoddan method_local_refharus disorot. methoddapat mengakses variabel global dan mencetaknya seperti pada 5. Global x. Tetapi method_local_reftidak bisa karena nanti mendefinisikan variabel lokal dengan nama yang sama. Anda dapat menguji ini dengan menghapus x = 200garis dan melihat perbedaannya
kiril
@ Brianray: Bagaimana dengan z?
Malik A. Rumi
@kiril Saya menambahkan catatan tentang itu
brianray
@ MalikA.Rumi Saya menghapus z karena tidak menarik
brianray
Anehnya, ini adalah satu - satunya penjelasan yang jelas tentang cakupan Python, yang dapat saya temukan di semua SO. Cukup menggunakan contoh yang sangat mendasar. Terima kasih!
not2qubit
13

Python menyelesaikan variabel Anda dengan - umumnya - tiga ruang nama yang tersedia.

Setiap saat selama eksekusi, setidaknya ada tiga cakupan bersarang yang ruang nama-nya dapat diakses secara langsung: ruang lingkup terdalam, yang dicari terlebih dahulu, berisi nama-nama lokal; ruang nama dari setiap fungsi penutup, yang dicari dimulai dengan lingkup penutup terdekat; lingkup tengah, dicari selanjutnya, berisi nama global modul saat ini; dan ruang lingkup terluar (dicari terakhir) adalah namespace yang mengandung nama bawaan.

Ada dua fungsi: globalsdan localsyang menunjukkan Anda isi dua ruang nama ini.

Ruang nama dibuat oleh paket, modul, kelas, konstruksi objek, dan fungsi. Tidak ada rasa ruang nama lain.

Dalam hal ini, panggilan ke fungsi yang dinamai xharus diselesaikan dalam ruang nama lokal atau ruang nama global.

Lokal dalam hal ini, adalah fungsi tubuh metode Foo.spam.

Global - baik - global.

Aturannya adalah untuk mencari ruang lokal bersarang yang dibuat oleh fungsi metode (dan definisi fungsi bersarang), lalu cari global. Itu dia.

Tidak ada ruang lingkup lain. The forpernyataan (dan pernyataan majemuk lain seperti ifdan try) tidak menciptakan lingkup bersarang baru. Hanya definisi (paket, modul, fungsi, kelas, dan instance objek.)

Di dalam definisi kelas, nama-nama adalah bagian dari namespace kelas. code2, misalnya, harus memenuhi syarat dengan nama kelas. Umumnya Foo.code2. Namun, self.code2juga akan berfungsi karena objek Python melihat kelas yang berisi sebagai back-back.

Objek (turunan dari sebuah kelas) memiliki variabel turunan. Nama-nama ini berada di namespace objek. Mereka harus memenuhi syarat oleh objek. ( variable.instance.)

Dari dalam metode kelas, Anda memiliki penduduk lokal dan global. Anda mengatakan self.variableuntuk memilih contoh sebagai namespace. Anda akan mencatat bahwa selfini adalah argumen untuk setiap fungsi anggota kelas, menjadikannya bagian dari namespace lokal.

Lihat Python Aturan Lingkup , Python Lingkup , Ruang Lingkup Variabel .

S.Lott
sumber
5
Ini sudah ketinggalan zaman. Sejak 2.1 (7 tahun yang lalu) ada lebih dari dua cakupan, karena fungsi bersarang memperkenalkan lingkup baru, sehingga fungsi dalam suatu fungsi akan memiliki akses ke lingkup lokal, lingkup fungsi melampirkan, dan cakupan global (juga builtin).
Brian
Maaf, ini bukan lagi masalahnya. Python has two namespaces available. Global and local-to-something.
Rizwan Kassim
9

Di mana x ditemukan?

x tidak ditemukan karena Anda belum mendefinisikannya. :-) Ini bisa ditemukan di code1 (global) atau code3 (lokal) jika Anda meletakkannya di sana.

code2 (anggota kelas) tidak terlihat oleh kode di dalam metode dari kelas yang sama - Anda biasanya akan mengaksesnya menggunakan sendiri. code4 / code5 (loop) hidup dalam lingkup yang sama dengan code3, jadi jika Anda menulis ke x di sana Anda akan mengubah instance x yang didefinisikan dalam code3, bukan membuat x baru.

Python memiliki cakupan yang statis, jadi jika Anda meneruskan 'spam' ke fungsi lain, spam masih akan memiliki akses ke global dalam modul asalnya (didefinisikan dalam kode1), dan cakupan lain yang berisi (lihat di bawah). anggota kode2 akan diakses lagi melalui self.

lambda tidak berbeda dengan def. Jika Anda memiliki lambda yang digunakan di dalam suatu fungsi, itu sama dengan mendefinisikan fungsi bersarang. Dalam Python 2.2 dan seterusnya, cakupan bersarang tersedia. Dalam hal ini, Anda dapat mengikat x pada level fungsi apa saja dan Python akan mengambil instance paling dalam:

x= 0
def fun1():
    x= 1
    def fun2():
        x= 2
        def fun3():
            return x
        return fun3()
    return fun2()
print fun1(), x

2 0

fun3 melihat instance x dari lingkup berisi terdekat, yang merupakan lingkup fungsi yang terkait dengan fun2. Tetapi x instance lainnya, yang didefinisikan dalam fun1 dan global, tidak terpengaruh.

Sebelum nested_scopes - dengan Python pre-2.1, dan 2.1 kecuali Anda secara khusus meminta fitur menggunakan impor dari masa depan - cakupan fun1 dan fun2 tidak terlihat fun3, jadi jawaban S.Lott berlaku dan Anda akan mendapatkan global x :

0 0
bobince
sumber
1

Dengan Python,

variabel apa pun yang diberi nilai adalah lokal ke blok tempat tugas tersebut muncul.

Jika variabel tidak dapat ditemukan dalam lingkup saat ini, silakan merujuk ke urutan LEGB.

GraceMeng
sumber