Krótki opis zasad określania zakresu?

472

Jakie dokładnie są reguły zakresu Pythona?

Jeśli mam jakiś kod:

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

Gdzie jest xznaleziony Niektóre możliwe opcje obejmują poniższą listę:

  1. W załączonym pliku źródłowym
  2. W przestrzeni nazw klas
  3. W definicji funkcji
  4. W zmiennej indeksu pętli for
  5. Wewnątrz pętli for

Istnieje również kontekst podczas wykonywania, gdy funkcja spamjest przekazywana gdzie indziej. A może funkcje lambda przebiegają nieco inaczej?

Musi być gdzieś proste odwołanie lub algorytm. To zagmatwany świat dla pośrednich programistów Pythona.

Charles Merriam
źródło
2
Zasady określania zakresu są dość zwięźle - ale także całkowicie - w dokumentacji Pythona: docs.python.org/3/reference/… .
jefe2000

Odpowiedzi:

420

Właściwie, zwięzła reguła dla rozdzielczości Python Scope, z Learning Python, 3. miejsce. Ed. . (Te reguły są specyficzne dla nazw zmiennych, a nie atrybutów. Jeśli odwołujesz się do nich bez kropki, obowiązują te reguły).

Reguła LEGB

  • L ocal - Nazwy przypisane w jakikolwiek sposób w ramach funkcji ( deflub lambda) i niezadeklarowane jako globalne w tej funkcji

  • E nclosing funkcja - Nazwy przypisane w lokalnym zakresie dowolny i wszystkie funkcje statycznie obejmującego ( defi lambda) od wewnętrznej do zewnętrznej

  • G lobal (moduł) - Nazwy przypisane na najwyższym poziomie pliku modułu lub przez wykonanie globalinstrukcji w defpliku

  • B uilt-in (Python) - Nazwy wstępnie przypisane w wbudowany moduł nazwami: open, range, SyntaxError, etc

Tak więc w przypadku

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

forPętla nie ma własną przestrzeń nazw. W kolejności LEGB zakresy byłyby

  • L: lokalny def spam(w code3, code4i code5)
  • E: Wszelkie otaczające funkcje (jeśli cały przykład był w innym def)
  • G: Czy xw module (w code1) były jakieś zadeklarowane globalnie ?
  • B: Dowolne wbudowane xw Python.

xnigdy nie zostanie znaleziony w code2(nawet w przypadkach, w których można się tego spodziewać, zobacz odpowiedź Antti lub tutaj ).

Rizwan Kassim
źródło
45
Jako zastrzeżenie dotyczące globalnego dostępu - odczyt zmiennej globalnej może się zdarzyć bez wyraźnej deklaracji, ale zapisanie jej bez deklaracji globalnej (nazwa_wariatu) spowoduje utworzenie nowej instancji lokalnej.
Peter Gibson
12
W rzeczywistości @Peter global(var_name)jest niepoprawny pod względem składniowym. Prawidłowa składnia byłaby global var_namebez nawiasów. Masz jednak ważny punkt.
martineau
Jeśli tak, to dlaczego zmienna „y” foo nie jest widoczna dla „paska” poniżej: >>> def foo(x): ... y = x ... def bar(z): ... y = z ... bar(5) ... print x,y ... >>> foo(3) 3 3
Jonathan Mayer
3
@Jathanathan: Ponieważ każdy yjest zapisywany i nie ma żadnych global ydeklaracji - patrz komentarz @ Piotra.
martineau,
@LakshmanPrasad Wpada w „E”, ale ma jedno szczególne zachowanie, o którym warto wspomnieć: jest to zmienna klasowa, więc jest „globalna” wśród swoich obiektów. Przypisanie go spowoduje nieprzewidziane i trudne do debugowania problemy, jeśli nie wiesz, co robisz.
Ctrl-C
157

Zasadniczo jedyną rzeczą w Pythonie, która wprowadza nowy zakres, jest definicja funkcji. Klasy są trochę szczególnym przypadkiem, w którym wszystko zdefiniowane bezpośrednio w ciele jest umieszczane w przestrzeni nazw klasy, ale nie są one bezpośrednio dostępne z poziomu metod (lub klas zagnieżdżonych), które zawierają.

W twoim przykładzie są tylko 3 zakresy, w których x będzie wyszukiwane:

  • zakres spamu - zawierający wszystko zdefiniowane w code3 i code5 (a także code4, twoja zmienna pętli)

  • Zakres globalny - zawierający wszystko zdefiniowane w kodzie 1, a także Foo (i wszelkie zmiany po nim)

  • Wbudowana przestrzeń nazw. Trochę specjalnego przypadku - zawiera różne wbudowane funkcje i typy Pythona, takie jak len () i str (). Zasadniczo nie powinien być modyfikowany przez żaden kod użytkownika, więc spodziewaj się, że będzie zawierał standardowe funkcje i nic więcej.

Więcej zakresów pojawia się tylko po wprowadzeniu do obrazu funkcji zagnieżdżonej (lub lambda). Będą jednak zachowywać się tak, jak można się było spodziewać. Zagnieżdżona funkcja może uzyskać dostęp do wszystkiego w zasięgu lokalnym, a także do wszystkiego w zasięgu funkcji zamykającej. na przykład.

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

Ograniczenia:

Można uzyskać dostęp do zmiennych w zakresach innych niż zmienne funkcji lokalnej, ale nie można ich przywrócić do nowych parametrów bez dalszej składni. Zamiast tego przypisanie utworzy nową zmienną lokalną zamiast wpływać na zmienną w zakresie nadrzędnym. Na przykład:

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

Aby faktycznie zmodyfikować powiązania zmiennych globalnych z zakresu funkcji, musisz określić, że zmienna jest globalna za pomocą słowa kluczowego global. Na przykład:

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

Obecnie nie ma sposobu, aby zrobić to samo dla zmiennych w zakresie zakresów funkcji , ale Python 3 wprowadza nowe słowo kluczowe „ nonlocal”, które będzie działać w sposób podobny do globalnego, ale dla zagnieżdżonych zakresów funkcji.

Brian
źródło
111

Nie było dokładnej odpowiedzi dotyczącej czasu Python3, więc udzieliłem odpowiedzi tutaj. Większość tego, co tu opisano, szczegółowo opisano w 4.2.2 Rozdzielczość nazw dokumentacji Python 3.

Jak podano w innych odpowiedziach, istnieją 4 podstawowe zakresy, LEGB, dla lokalnego, zamkniętego, globalnego i wbudowanego. Oprócz nich istnieje specjalny zakres, ciało klasy , które nie obejmuje zakresu obejmującego metody zdefiniowane w klasie; wszelkie przypisania w ciele klasy powodują, że zmienna jest odtąd związana w ciele klasy.

Zwłaszcza brak instrukcji blokowej poza tym defi classtworzenie zmiennego zakresu. W Pythonie 2 obsługa list nie tworzy zakresu zmiennych, jednak w Pythonie 3 zmienna pętli w ramach list jest tworzona w nowym zakresie.

Aby zademonstrować osobliwości ciała klasowego

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)

Zatem, inaczej niż w ciele funkcji, można ponownie przypisać zmienną do tej samej nazwy w ciele klasy, aby uzyskać zmienną klasy o tej samej nazwie; dalsze wyszukiwania tej nazwy rozstrzygają zamiast tego zmienną klasy.


Jedną z większych niespodzianek dla wielu nowych użytkowników Pythona jest to, że forpętla nie tworzy zakresu zmiennego. W Pythonie 2 wyrażenia listowe również nie tworzą zakresu (podczas gdy generatory i wyrażenia dyktują!). Zamiast tego przeciekają wartość w funkcji lub zasięgu globalnym:

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

Wyjaśnienia mogą być używane jako sprytny (lub okropny, jeśli chcesz) sposób modyfikowania zmiennych w wyrażeniach lambda w Pythonie 2 - wyrażenie lambda tworzy zakres zmiennych, podobnie jak definstrukcja, ale w lambda żadne instrukcje nie są dozwolone. Przypisanie będące instrukcją w Pythonie oznacza, że ​​żadne przypisania zmiennych w lambda nie są dozwolone, ale rozumienie listy jest wyrażeniem ...

To zachowanie zostało naprawione w Pythonie 3 - brak wyrażeń związanych ze zrozumieniem lub zmiennych wyciekających z generatorów.


Globalny naprawdę oznacza zakres modułu; głównym modułem Pythona jest __main__; wszystkie importowane moduły są dostępne poprzez sys.moduleszmienną; aby uzyskać dostęp do __main__jednego z nich sys.modules['__main__'], lub import __main__; dostęp do nich i przypisywanie atrybutów jest całkowicie do przyjęcia; pojawią się jako zmienne w globalnym zakresie modułu głównego.


Jeśli nazwa zostanie kiedykolwiek przypisana w bieżącym zakresie (z wyjątkiem zakresu klasy), zostanie uznana za należącą do tego zakresu, w przeciwnym razie zostanie uznana za należącą do dowolnego obejmującego zakresu, który przypisuje zmiennej (może nie zostać przypisana jeszcze, albo wcale), czy wreszcie zasięg globalny. Jeśli zmienna jest uważana za lokalną, ale nie jest jeszcze ustawiona lub została usunięta, odczyt wartości zmiennej spowoduje UnboundLocalError, co jest podklasą 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()

Zakres może zadeklarować, że wyraźnie chce zmodyfikować zmienną globalną (zakres modułu) za pomocą słowa kluczowego global:

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

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

Jest to również możliwe, nawet jeśli został ukryty w zakresie:

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

W Pythonie 2 nie ma łatwego sposobu na modyfikację wartości w otaczającym zakresie; zwykle jest to symulowane przez wartość zmienną, taką jak lista o długości 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

Jednak w Pythonie 3 nonlocalprzychodzi na ratunek:

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.

nonlocalDokumentacja mówi, że

Nazwy wymienione w instrukcji nielokalnej, w przeciwieństwie do tych wymienionych w instrukcji globalnej, muszą odnosić się do wcześniej istniejących powiązań w zakresie obejmującym (zakresu, w którym należy utworzyć nowe powiązanie, nie można jednoznacznie określić).

tzn. nonlocalzawsze odnosi się do najbardziej wewnętrznego zewnętrznego zakresu nieglobalnego, w którym nazwa została powiązana (tj. przypisana, w tym używana jako forzmienna docelowa, w withklauzuli lub jako parametr funkcji).


Każda zmienna, która nie jest uważana za lokalną dla bieżącego zakresu lub dowolnego obejmującego zakresu, jest zmienną globalną. Globalna nazwa znajduje się w globalnym słowniku modułu; jeśli nie zostanie znaleziony, globalny jest następnie sprawdzany z wbudowanego modułu; nazwa modułu została zmieniona z python 2 na python 3; tak było w Pythonie 2, __builtin__a teraz w Pythonie 3 builtins. Jeśli przypiszesz atrybut wbudowanemu modułowi, będzie on później widoczny dla dowolnego modułu jako czytelna zmienna globalna, chyba że moduł ten ocienia je własną zmienną globalną o tej samej nazwie.


Przydatny może być także odczyt wbudowanego modułu; załóżmy, że chcesz funkcji drukowania w stylu Pythona 3 w niektórych częściach pliku, ale inne części pliku nadal używają printinstrukcji. W Pythonie 2.6-2.7 można uzyskać funkcję Python 3 za printpomocą:

import __builtin__

print3 = __builtin__.__dict__['print']

from __future__ import print_functionFaktycznie nie importuje się printnigdzie funkcji w Pythonie 2 - zamiast po prostu wyłącza zasady analizowania na printrachunku w bieżącym module obsługi printjak każdy inny identyfikator zmiennej, a tym samym pozwalając printfunkcja być spojrzał w builtins.

Antti Haapala
źródło
23

Reguły określania zakresu dla Python 2.x zostały już przedstawione w innych odpowiedziach. Jedyne, co chciałbym dodać, to to, że w Pythonie 3.0 istnieje również koncepcja zakresu nielokalnego (wskazywanego przez słowo kluczowe „nielokalne”). Pozwala to na bezpośredni dostęp do zewnętrznych zakresów i otwiera możliwość wykonywania pewnych schludnych sztuczek, w tym leksykalnych zamknięć (bez brzydkich hacków z udziałem zmiennych obiektów).

EDYCJA: Oto PEP z więcej informacji na ten temat.

Jeremy Cantrell
źródło
23

Nieco bardziej kompletny przykład zakresu:

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()

wynik:

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
źródło
6
To świetna odpowiedź. Myślę jednak, że należy podkreślić różnice między methodi method_local_ref. methodjest w stanie uzyskać dostęp do zmiennej globalnej i wydrukować ją jak w 5. Global x. Ale method_local_refnie może, ponieważ później definiuje zmienną lokalną o tej samej nazwie. Możesz to przetestować, usuwając x = 200wiersz i zobaczyć różnicę
kiril
@brianray: Co z Z?
Malik A. Rumi
@kiril Dodałem notatkę na ten temat
brianray
@ MalikA.Rumi Usunąłem Z, ponieważ nie było to interesujące
brianray
Zaskakujące jest to jedyne jasne wyjaśnienie zakresów Pythona, które mogłem znaleźć na wszystkich SO. Po prostu używając bardzo podstawowego przykładu. Dzięki!
not2qubit
13

Python rozwiązuje twoje zmienne za pomocą - ogólnie - trzech dostępnych przestrzeni nazw.

W dowolnym momencie podczas wykonywania istnieją co najmniej trzy zagnieżdżone zakresy, których przestrzenie nazw są bezpośrednio dostępne: najbardziej wewnętrzny zakres, który jest przeszukiwany jako pierwszy, zawiera nazwy lokalne; przestrzenie nazw wszystkich obejmujących funkcji, które są wyszukiwane zaczynając od najbliższego obejmującego zakresu; środkowy zakres, przeszukiwany następnie, zawiera globalne nazwy bieżącego modułu; a najbardziej zewnętrznym zakresem (ostatnio wyszukiwanym) jest przestrzeń nazw zawierająca wbudowane nazwy.

Istnieją dwie funkcje: globalsi localsktóre pokazują zawartość dwóch z tych przestrzeni nazw.

Przestrzenie nazw są tworzone przez pakiety, moduły, klasy, konstrukcję obiektów i funkcje. Nie ma innych smaków przestrzeni nazw.

W takim przypadku wywołanie funkcji o nazwie xmusi zostać rozstrzygnięte w lokalnej przestrzeni nazw lub globalnej przestrzeni nazw.

Lokalny w tym przypadku jest ciałem funkcji metody Foo.spam.

Globalny jest - cóż - globalny.

Zasadą jest przeszukiwanie zagnieżdżonych lokalnych przestrzeni utworzonych przez funkcje metod (i definicje zagnieżdżonych funkcji), a następnie przeszukiwanie globalne. Otóż ​​to.

Nie ma innych zakresów. forOświadczenie (oraz inne oświadczenia złożone jak ifi try) nie tworzą nowych zagnieżdżonych zasięgów. Tylko definicje (pakiety, moduły, funkcje, klasy i instancje obiektów).

W definicji klasy nazwy są częścią przestrzeni nazw klasy. code2, na przykład, musi być kwalifikowany nazwą klasy. Ogólnie Foo.code2. Działa jednak self.code2również, ponieważ obiekty Python patrzą na zawierającą klasę jako rezerwową.

Obiekt (instancja klasy) ma zmienne instancji. Te nazwy znajdują się w przestrzeni nazw obiektu. Muszą być kwalifikowane przez obiekt. ( variable.instance.)

Z metody klasowej masz lokalne i globalne. Mówisz, self.variableaby wybrać instancję jako przestrzeń nazw. Zauważysz, że selfjest to argument każdej funkcji członka klasy, co czyni ją częścią lokalnej przestrzeni nazw.

Zobacz Python Rules Scope , Python Zakres , zasięg zmiennych .

S.Lott
źródło
5
To jest przeterminowane. Od wersji 2.1 (7 lat temu) istnieją więcej niż dwa zakresy, ponieważ funkcje zagnieżdżone wprowadzają nowe zakresy, więc funkcja w ramach funkcji będzie miała dostęp do zakresu lokalnego, zakresu funkcji obejmujących i zakresu globalnego (także wbudowanych).
Brian
Przepraszam, już tak nie jest. Python has two namespaces available. Global and local-to-something.
Rizwan Kassim
9

Gdzie znajduje się x?

x nie został znaleziony, ponieważ go nie zdefiniowałeś. :-) Można go znaleźć w code1 (globalny) lub code3 (lokalny), jeśli go tam umieścisz.

code2 (członkowie klasy) nie są widoczne dla kodu w metodach tej samej klasy - zwykle uzyskiwałbyś do nich dostęp za pomocą self. code4 / code5 (pętle) żyją w tym samym zakresie co code3, więc jeśli napisałeś do x tam, zmieniłbyś instancję x zdefiniowaną w code3, nie tworząc nowego x.

Python ma zakres statyczny, więc jeśli przekażesz „spam” do innej funkcji, spam nadal będzie miał dostęp do globałów w module, z którego pochodzi (zdefiniowany w kodzie 1), i do wszystkich innych zawierających zakresy (patrz poniżej). Członkowie code2 będą ponownie dostępni przez siebie.

lambda nie różni się od def. Jeśli w funkcji jest używana lambda, jest to to samo, co definiowanie funkcji zagnieżdżonej. W Python 2.2 i nowszym dostępne są zagnieżdżone zakresy. W takim przypadku możesz powiązać x na dowolnym poziomie zagnieżdżenia funkcji, a Python wybierze najbardziej wewnętrzną instancję:

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

2 0

fun3 widzi instancję x z najbliższego zawierającego zakresu, który jest zakresem funkcji związanym z fun2. Ale nie dotyczy to innych instancji x, zdefiniowanych w fun1 i globalnie.

Przed zagnieżdżeniem_skopów - w Pythonie wcześniejszym niż 2.1 oraz w 2.1, chyba że poprosisz o tę funkcję przy użyciu importu z przyszłości - zakresy fun1 i fun2 nie są widoczne dla fun3, więc odpowiedź S.Lott pozostaje, a otrzymasz globalny x :

0 0
Bobin
źródło
1

W Pythonie

każda zmienna, której przypisano wartość, jest lokalna dla bloku, w którym pojawia się przypisanie.

Jeśli zmiennej nie można znaleźć w bieżącym zakresie, zapoznaj się z kolejnością LEGB.

GraceMeng
źródło