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 x
znaleziony Niektóre możliwe opcje obejmują poniższą listę:
- W załączonym pliku źródłowym
- W przestrzeni nazw klas
- W definicji funkcji
- W zmiennej indeksu pętli for
- Wewnątrz pętli for
Istnieje również kontekst podczas wykonywania, gdy funkcja spam
jest 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.
python
scope
dynamic-languages
Charles Merriam
źródło
źródło
Odpowiedzi:
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 (
def
lublambda
) i niezadeklarowane jako globalne w tej funkcjiE nclosing funkcja - Nazwy przypisane w lokalnym zakresie dowolny i wszystkie funkcje statycznie obejmującego (
def
ilambda
) od wewnętrznej do zewnętrznejG lobal (moduł) - Nazwy przypisane na najwyższym poziomie pliku modułu lub przez wykonanie
global
instrukcji wdef
plikuB uilt-in (Python) - Nazwy wstępnie przypisane w wbudowany moduł nazwami:
open
,range
,SyntaxError
, etcTak więc w przypadku
for
Pętla nie ma własną przestrzeń nazw. W kolejności LEGB zakresy byłybydef spam
(wcode3
,code4
icode5
)def
)x
w module (wcode1
) były jakieś zadeklarowane globalnie ?x
w Python.x
nigdy nie zostanie znaleziony wcode2
(nawet w przypadkach, w których można się tego spodziewać, zobacz odpowiedź Antti lub tutaj ).źródło
global(var_name)
jest niepoprawny pod względem składniowym. Prawidłowa składnia byłabyglobal var_name
bez nawiasów. Masz jednak ważny punkt.>>> def foo(x): ... y = x ... def bar(z): ... y = z ... bar(5) ... print x,y ... >>> foo(3) 3 3
y
jest zapisywany i nie ma żadnychglobal y
deklaracji - patrz komentarz @ Piotra.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.
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:
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:
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.źródło
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
def
iclass
tworzenie 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
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
for
pę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: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
def
instrukcja, 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 poprzezsys.modules
zmienną; aby uzyskać dostęp do__main__
jednego z nichsys.modules['__main__']
, lubimport __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
.Zakres może zadeklarować, że wyraźnie chce zmodyfikować zmienną globalną (zakres modułu) za pomocą słowa kluczowego global:
Jest to również możliwe, nawet jeśli został ukryty w zakresie:
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:
Jednak w Pythonie 3
nonlocal
przychodzi na ratunek:nonlocal
Dokumentacja mówi, żetzn.
nonlocal
zawsze odnosi się do najbardziej wewnętrznego zewnętrznego zakresu nieglobalnego, w którym nazwa została powiązana (tj. przypisana, w tym używana jakofor
zmienna docelowa, wwith
klauzuli 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 3builtins
. 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ą
print
instrukcji. W Pythonie 2.6-2.7 można uzyskać funkcję Python 3 zaprint
pomocą:from __future__ import print_function
Faktycznie nie importuje sięprint
nigdzie funkcji w Pythonie 2 - zamiast po prostu wyłącza zasady analizowania naprint
rachunku w bieżącym module obsługiprint
jak każdy inny identyfikator zmiennej, a tym samym pozwalającprint
funkcja być spojrzał w builtins.źródło
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.
źródło
Nieco bardziej kompletny przykład zakresu:
wynik:
źródło
method
imethod_local_ref
.method
jest w stanie uzyskać dostęp do zmiennej globalnej i wydrukować ją jak w5. Global x
. Alemethod_local_ref
nie może, ponieważ później definiuje zmienną lokalną o tej samej nazwie. Możesz to przetestować, usuwającx = 200
wiersz i zobaczyć różnicęPython rozwiązuje twoje zmienne za pomocą - ogólnie - trzech dostępnych przestrzeni nazw.
Istnieją dwie funkcje:
globals
ilocals
któ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
x
musi 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.
for
Oświadczenie (oraz inne oświadczenia złożone jakif
itry
) 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ólnieFoo.code2
. Działa jednakself.code2
ró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.variable
aby wybrać instancję jako przestrzeń nazw. Zauważysz, żeself
jest 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 .
źródło
Python has two namespaces available. Global and local-to-something.
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ę:
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 :
źródło
W Pythonie
Jeśli zmiennej nie można znaleźć w bieżącym zakresie, zapoznaj się z kolejnością LEGB.
źródło