Krótki opis zasad ustalania zakresu?

Jakie Dokładnie są reguły zakresów Pythona?

Jeśli mam jakiś kod:

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

Gdzie znajduje się x? Niektóre możliwe opcje obejmują powyższą listę:

  1. w załączonym pliku źródłowym
  2. w przestrzeni nazw klasy
  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 spam jest przekazywana gdzie indziej. A może funkcje lambda przechodzą nieco inaczej?

Musi być gdzieś proste odniesienie lub algorytm. To zagmatwany świat dla średnio zaawansowanych programistów Pythona.

Author: martineau, 2008-11-15

7 answers

Właściwie, zwięzła Reguła dla rozdzielczości zakresu Pythona, z Learning Python, 3rd. Ed.. (Reguły te są specyficzne dla nazw zmiennych, a nie atrybutów. Jeśli odwołujesz się do niego bez okresu, obowiązują te zasady)

Reguła LEGB.

L, Local - nazwy przypisane w jakikolwiek sposób w ramach funkcji (def lub lambda)), a nie zadeklarowane jako globalne w tej funkcji.

E , Enclosing-function - Name in the local scope of any and all statycznie funkcje zamykające (def lub lambda), od wewnętrznej do zewnętrznej.

G, Global (module) - nazwy przypisane na najwyższym poziomie pliku modułu lub poprzez wykonanie instrukcji global W def wewnątrz pliku.

B, Wbudowany (Python) - nazwy wstępnie przypisane w module wbudowanych nazw: open,range,SyntaxError,...

Tak więc w przypadku

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

Pętla for Nie ma własnej przestrzeni nazw. W kolejności LEGB lunety będą

L : lokalny, w def spam (w code3, code 4, code5).

E: funkcja zamknięta, dowolne funkcje zamykające (jeśli cały przykład był w innym def)

G : Global. Czy w module (code1 były jakieś x zgłoszone globalnie?

B: dowolny wbudowany x w Pythonie.

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

 357
Author: Rizwan Kassim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:41

Zasadniczo, jedyną rzeczą w Pythonie, która wprowadza nowy zakres, jest definicja funkcji. Klasy są nieco szczególnym przypadkiem w tym, że cokolwiek zdefiniowane bezpośrednio w ciele jest umieszczane w przestrzeni nazw klasy, ale nie są bezpośrednio dostępne z wewnątrz 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)

  • Zasięg globalny-zawierający wszystko co jest zdefiniowane w code1, a także Foo (i wszystko co się po nim zmienia)

  • Przestrzeń nazw builtins. Trochę specjalnego przypadku-zawiera różne funkcje i typy Pythona, takie jak len () i str(). Generalnie nie powinno to być modyfikowane przez żaden kod użytkownika, więc spodziewaj się, że będzie zawierać standardowe funkcje i nic więcej.

Więcej lunet pojawia się tylko wtedy, gdy wprowadzasz zagnieżdżona funkcja (lub lambda) do obrazu. Będą one zachowywać się prawie tak, jak można się jednak spodziewać. Zagnieżdżona funkcja może uzyskać dostęp do wszystkiego z zakresu lokalnego, jak również wszystkiego z zakresu funkcji zamykającej. np.

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

Ograniczenia:

Zmienne w zakresach innych niż zmienne lokalnej funkcji mogą być dostępne, ale nie mogą być przywracane do nowych parametrów bez dalszej składni. Zamiast tego, assignment utworzy nową zmienną local zamiast wpływający 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 rzeczywiście zmodyfikować powiązania globalnych zmiennych z zakresu funkcji, musisz określić, że zmienna jest globalna za pomocą słowa kluczowego global. Eg:

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

Obecnie nie ma możliwości zrobienia tego samego dla zmiennych w zakresach function , ale Python 3 wprowadza nowe słowo kluczowe, " nonlocal", które będzie działać podobnie do globalnych, ale dla zagnieżdżonych zakresów funkcji.

 138
Author: Brian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-15 21:51:09

Nie było dokładnej odpowiedzi dotyczącej czasu Python3, więc zrobiłem odpowiedź tutaj.

Jak podano w innych odpowiedziach, istnieją 4 podstawowe zakresy, LEGB, dla lokalnego, zamkniętego, globalnego i wbudowanego. Oprócz tych, istnieje specjalny zakres, ciało klasy, który nie obejmuje zakresu zamkniętego dla metod zdefiniowanych w klasie; wszelkie przydziały wewnątrz ciała klasy sprawiają, że zmienna jest odtąd związana w ciele klasy.

Szczególnie, nie Instrukcja blokowa, poza def i class, tworzy zmienny zakres. W Pythonie 2 zrozumienie listy nie tworzy zakresu zmiennej, jednak w Pythonie 3 zmienna pętli jest tworzona w nowym zakresie.

Aby zademonstrować specyfikę 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)

Tak więc w przeciwieństwie do ciała funkcji, możesz przypisać zmienną do tej samej nazwy w ciele klasy, aby uzyskać zmienną klasy o tej samej nazwie; dalsze poszukiwania tej nazwy rozwiązują do zmiennej klasy zamiast tego.


Jedną z większych niespodzianek dla wielu nowych Użytkowników Pythona jest to, że pętla for nie tworzy zakresu zmiennej. W Pythonie 2 składanie list również nie tworzy zakresu (podczas gdy generatory i składanie dict tak!) Zamiast tego wyciekają wartość w funkcji lub globalnym zasięgu:

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

Składanie może być użyte jako sprytny (lub okropny, jeśli chcesz) sposób na modyfikowanie zmiennych w wyrażeniach lambda w Pythonie 2-wyrażenie lambda tworzy zmienny zakres, tak jak instrukcja def would, ale w lambda żadne polecenia nie są dozwolone. Przypisanie jako polecenie w Pythonie oznacza, że w lambda nie jest dozwolone przypisywanie zmiennych, ale zrozumienie listy jest wyrażeniem...

To zachowanie zostało naprawione w Pythonie 3 - żadne wyrażenia rozumienia ani Generatory nie wyciekają zmiennych.


Globalny tak naprawdę oznacza zakres modułu; głównym modułem Pythona jest __main__; wszystkie importowane moduły są dostępne przez zmienną sys.modules; aby uzyskać dostęp do __main__ można użyć sys.modules['__main__'], lub import __main__; jest całkowicie dopuszczalne, aby uzyskać dostęp i przypisać atrybuty tam; będą one wyświetlane jako zmienne w globalnym zasięgu głównego modułu.


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

Scope może zadeklarować, że jawnie chce zmodyfikować zmienną globalną (module scope), używając słowa kluczowego global:

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

foobar()
print(x) # -> 6
Jest to również możliwe, nawet jeśli zostało ono zasłonięte w otaczającym 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()      # -> print 5 911
print(x, y) # -> 6 13

W Pythonie 2 nie ma łatwej drogi jest to możliwe dzięki zmianie wartości, np. listy 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, nonlocal przychodzi 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.

Każda zmienna, która nie jest uznawana za lokalną dla bieżącego zakresu, lub Dowolna zmienna otaczająca, jest zmienną globalną. Globalna nazwa jest wyszukiwana w module global dictionary; jeśli nie została znaleziona, globalna jest następnie wyszukiwana z wbudowanego modułu; Nazwa modułu była zmieniono z Pythona 2 na Pythona 3; w Pythonie 2 był to __builtin__, a w Pythonie 3 jest teraz nazywany builtins. Jeśli przypiszesz do atrybutu modułu builtins, będzie on widoczny dla dowolnego modułu jako czytelna zmienna globalna, chyba że moduł zasłania je własną zmienną globalną o tej samej nazwie.


Czytanie wbudowanego modułu może być również przydatne; Załóżmy, że chcesz mieć funkcję drukowania w stylu Pythona 3 w niektórych częściach pliku, ale inne części pliku nadal używają print jeśli Twoja wersja Pythona jest > = 2.6, możesz uzyskać nową funkcję stylu jako:

import __builtin__

print3 = __builtin__.__dict__['print']

from __future__ import print_function w rzeczywistości nie importuje funkcji print nigdzie w Pythonie 2 - zamiast tego wyłącza reguły parsowania dla instrukcji print w bieżącym module, obsługując print jak każdy inny identyfikator zmiennej, a tym samym pozwalając na wyszukanie print funkcji w wbudowanym module.

 87
Author: Antti Haapala,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-22 08:31:51

Zasady ustalania zakresu dla Pythona 2.x zostały już opisane w innych odpowiedziach. Jedyną rzeczą, którą chciałbym dodać, jest to, że w Pythonie 3.0, istnieje również pojęcie zakresu nielokalnego(wskazanego przez słowo kluczowe 'nonlocal'). Pozwala to na bezpośredni dostęp do zewnętrznych zakresów i otwiera możliwość wykonywania ciekawych trików, w tym leksykalnych zamknięć (bez brzydkich hacków z udziałem mutowalnych obiektów).

EDIT: Oto PEP z Więcej informacji na ten temat.

 20
Author: Jeremy Cantrell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-15 18:52:49

Nieco pełniejszy 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()

Wyjście:

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
 20
Author: brianray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-20 04:04:39

Python rozwiązuje zmienne z -- ogólnie -- trzema dostępnymi przestrzeniami nazw.

W każdej chwili podczas egzekucji, tam są co najmniej trzema zagnieżdżonymi lunetami, których przestrzenie nazw są bezpośrednio dostępne: najskrytszy zakres, który jest przeszukiwany po pierwsze, zawiera nazwy miejscowe; przestrzenie nazw dowolnych funkcji zamykających, które są wyszukiwane począwszy od najbliższy zakres zamykający; środkowy zakres, przeszukany dalej, zawiera globalne nazwy bieżącego modułu; oraz na najbardziej odległy zakres (przeszukiwany ostatnio) to przestrzeń nazw zawierająca wbudowane nazwy.

Istnieją dwie funkcje: globals i locals, które pokazują zawartość dwóch z tych przestrzeni nazw.

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

W tym przypadku wywołanie funkcji o nazwie {[2] } musi zostać rozwiązane w lokalnej przestrzeni nazw lub globalnej przestrzeni nazw.

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

Globalny jest -- cóż -- globalny.

Regułą jest przeszukiwanie zagnieżdżonych przestrzeni lokalnych utworzonych przez funkcje metody( i zagnieżdżone definicje funkcji), a następnie przeszukiwanie globalne. To wszystko.

Nie ma innych zakresów. Instrukcja for (i inne złożone instrukcje, takie jak if i try) nie tworzą nowych zagnieżdżonych zakresów. Tylko definicje (Pakiety, Moduły, funkcje, klasy i instancje obiektów.)

Wewnątrz definicja klasy, nazwy są częścią przestrzeni nazw klas. code2, na przykład, musi być zakwalifikowana przez nazwę klasy. Ogólnie Foo.code2. Jednak self.code2 będzie również działać, ponieważ obiekty Pythona patrzą na klasę zawierającą w sobie jako alternatywę.

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

Z wewnątrz metody klasowej, masz mieszkańców i globali. Mówisz self.variable do wybierz instancję jako przestrzeń nazw. Zauważ, że self jest argumentem każdej funkcji członka klasy, co czyni ją częścią lokalnej przestrzeni nazw.

Zobacz Zasady Zakresu Pythona, Zakres Pythona, Zakres Zmienny .

 11
Author: S.Lott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:44

Gdzie znajduje się X?

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

Code2 (Członkowie klasy) nie są widoczne dla kodu wewnątrz metod tej samej klasy - Zwykle uzyskujesz do nich dostęp za pomocą self. code4 / code5 (pętle) żyją w tym samym zakresie co code3, więc jeśli piszesz tam do x, zmienisz instancję x zdefiniowaną w code3, nie tworząc nowego x.

Python jest statycznie scoped, więc jeśli przekażesz "spam" do innej funkcji, spam nadal będzie miał dostęp do globali w module, z którego pochodzi (zdefiniowanym w code1), oraz wszelkich innych zawierających zakresy (patrz poniżej). członkowie code2 będą ponownie dostępni za pośrednictwem self.

Lambda nie różni się od def. Jeśli wewnątrz funkcji jest używana funkcja lambda, jest to takie samo jak definiowanie funkcji zagnieżdżonej. W Pythonie 2.2 dostępne są zagnieżdżone zakresy. W tym przypadku można bindować x na dowolnym poziomie zagnieżdżania funkcji i Python wybierze w górę instancji wewnętrznej:
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 zakresu zawierającego, który jest zakresem funkcji powiązanym z fun2. Ale inne instancje x, zdefiniowane w fun1 i globalnie, nie mają wpływu.

Before nested_scopes-w Pythonie pre-2.1, i w 2.1, chyba że poprosisz o tę funkcję za pomocą from-future - import-fun1 i lunety fun2 nie są widoczne dla fun3, więc odpowiedź S. Lotta trzyma się i dostaniesz globalne x:

0 0
 7
Author: bobince,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-15 12:44:59