Kurzbeschreibung der Scoping-Regeln?

472

Was genau sind die Python-Scoping-Regeln?

Wenn ich Code habe:

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

Wo ist zu xfinden? Einige mögliche Optionen sind die folgende Liste:

  1. In der beiliegenden Quelldatei
  2. Im Klassennamensraum
  3. In der Funktionsdefinition
  4. In der for-Schleifenindexvariablen
  5. Innerhalb der for-Schleife

Es gibt auch den Kontext während der Ausführung, wenn die Funktion spaman eine andere Stelle übergeben wird. Und vielleicht laufen Lambda-Funktionen etwas anders?

Es muss irgendwo eine einfache Referenz oder einen Algorithmus geben. Es ist eine verwirrende Welt für fortgeschrittene Python-Programmierer.

Charles Merriam
quelle
2
Die Scoping-Regeln werden in der Python-Dokumentation ziemlich knapp - aber auch vollständig - beschrieben: docs.python.org/3/reference/… .
jefe2000

Antworten:

420

Eigentlich eine prägnante Regel für die Auflösung von Python Scope aus Learning Python, 3 .. Ed. . (Diese Regeln gelten nur für Variablennamen und nicht für Attribute. Wenn Sie ohne Punkt darauf verweisen, gelten diese Regeln.)

LEGB-Regel

  • L Ocal - Namen innerhalb einer Funktion in irgendeiner Weise zugeordnet ( defoder lambda), und nicht deklarierte globale in dieser Funktion

  • E nclosing-Funktion - Namen, die im lokalen Bereich aller statisch einschließenden Funktionen ( defoder lambda) von innen nach außen zugewiesen werden

  • G lobal (Modul) - Namen auf der obersten Ebene einer Moduldatei zugeordnet ist , oder durch eine Ausführung von globalAnweisung in einem definnerhalb der Datei

  • B uilt-in (Python) - Namen vorbelegt im eingebauten Namen Modul: open, range, SyntaxError, usw.

Also im Fall von

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

Die forSchleife hat keinen eigenen Namespace. In der LEGB-Reihenfolge wären die Bereiche

  • L: Lokale in def spam(in code3, code4und code5)
  • E: Alle umschließenden Funktionen (wenn das gesamte Beispiel in einem anderen wäre def)
  • G: Wurden xim Modul (in code1) global deklariert ?
  • B: Alle xin Python eingebauten .

xwird niemals in gefunden werden code2(selbst in Fällen, in denen Sie dies erwarten könnten, siehe Anttis Antwort oder hier ).

Rizwan Kassim
quelle
45
Als Einschränkung für den globalen Zugriff kann das Lesen einer globalen Variablen ohne explizite Deklaration erfolgen. Wenn Sie jedoch ohne Deklaration von global (var_name) darauf schreiben, wird stattdessen eine neue lokale Instanz erstellt.
Peter Gibson
12
Eigentlich ist @Peter global(var_name)syntaktisch falsch. Die korrekte Syntax wäre global var_nameohne Klammern. Sie haben jedoch einen gültigen Punkt.
Martineau
Wenn ja, warum ist die "y" >>> def foo(x): ... y = x ... def bar(z): ... y = z ... bar(5) ... print x,y ... >>> foo(3) 3 3
Jonathan Mayer
3
@ Jonathan: Weil jeder ygeschrieben wird und es keine global yErklärungen gibt - siehe @ Peters Kommentar.
Martineau
@LakshmanPrasad Es fällt in "E", hat aber ein besonderes Verhalten, das es wert ist, erwähnt zu werden: Es ist eine Klassenvariable, also ein "globales" Objekt. Zuweisen es wird zu unpredicted und schwer zu debuggen Problemen führen , wenn Sie nicht wissen , was du tust.
Strg-C
157

Im Wesentlichen ist das einzige, was in Python einen neuen Bereich einführt, eine Funktionsdefinition. Klassen sind insofern ein Sonderfall, als alles, was direkt im Body definiert ist, im Namespace der Klasse platziert wird, aber nicht direkt über die darin enthaltenen Methoden (oder verschachtelten Klassen) zugänglich ist.

In Ihrem Beispiel gibt es nur 3 Bereiche, in denen x gesucht wird:

  • Spam-Bereich - enthält alles, was in Code3 und Code5 definiert ist (sowie Code4, Ihre Schleifenvariable).

  • Der globale Bereich - enthält alles, was in Code1 definiert ist, sowie Foo (und was sich danach ändert)

  • Der eingebaute Namespace. Ein Sonderfall - dieser enthält die verschiedenen in Python integrierten Funktionen und Typen wie len () und str (). Im Allgemeinen sollte dies nicht durch einen Benutzercode geändert werden. Erwarten Sie daher, dass dieser die Standardfunktionen und nichts anderes enthält.

Weitere Bereiche werden nur angezeigt, wenn Sie eine verschachtelte Funktion (oder Lambda) in das Bild einfügen. Diese verhalten sich jedoch so, wie Sie es erwarten würden. Die verschachtelte Funktion kann auf alles im lokalen Bereich sowie auf alles im Bereich der umschließenden Funktion zugreifen. z.B.

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

Beschränkungen:

Auf Variablen in anderen Bereichen als den Variablen der lokalen Funktion kann zugegriffen werden, sie können jedoch nicht ohne weitere Syntax auf neue Parameter zurückgesetzt werden. Stattdessen wird durch die Zuweisung eine neue lokale Variable erstellt, anstatt die Variable im übergeordneten Bereich zu beeinflussen. Zum Beispiel:

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

Um die Bindungen globaler Variablen innerhalb eines Funktionsbereichs tatsächlich zu ändern, müssen Sie angeben, dass die Variable mit dem globalen Schlüsselwort global ist. Z.B:

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

Derzeit gibt es keine Möglichkeit, dasselbe für Variablen in eingeschlossenen Funktionsbereichen zu tun. Python 3 führt jedoch ein neues Schlüsselwort ein, " nonlocal" das sich ähnlich wie globale, jedoch für verschachtelte Funktionsbereiche verhält.

Brian
quelle
111

Es gab keine gründliche Antwort bezüglich der Python3-Zeit, daher habe ich hier eine Antwort gegeben. Das meiste, was hier beschrieben wird, ist in der 4.2.2 Auflösung der Namen der Python 3-Dokumentation beschrieben.

Wie in anderen Antworten angegeben, gibt es 4 grundlegende Bereiche, den LEGB, für Local, Enclosing, Global und Builtin. Zusätzlich zu diesen gibt es einen speziellen Bereich, den Klassenkörper , der keinen umschließenden Bereich für innerhalb der Klasse definierte Methoden umfasst. Durch Zuweisungen innerhalb des Klassenkörpers wird die Variable von da an im Klassenkörper gebunden.

Insbesondere erstellt keine Blockanweisung außer defund classeinen variablen Bereich. In Python 2 erstellt ein Listenverständnis keinen Variablenbereich. In Python 3 wird jedoch die Schleifenvariable innerhalb des Listenverständnisses in einem neuen Bereich erstellt.

Die Besonderheiten des Klassenkörpers demonstrieren

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)

Im Gegensatz zum Funktionskörper können Sie die Variable daher im Klassenkörper demselben Namen zuweisen, um eine Klassenvariable mit demselben Namen zu erhalten. Weitere Suchvorgänge für diesen Namen werden stattdessen in die Klassenvariable aufgelöst.


Eine der größeren Überraschungen für viele Python-Neulinge ist, dass eine forSchleife keinen variablen Bereich erstellt. In Python 2 erstellen die Listenverständnisse auch keinen Bereich (während Generatoren und Diktverständnisse dies tun!). Stattdessen verlieren sie den Wert in der Funktion oder im globalen Bereich:

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

Das Verständnis kann als gerissene (oder schreckliche, wenn Sie so wollen) Methode verwendet werden, um modifizierbare Variablen in Lambda-Ausdrücken in Python 2 zu erstellen. Ein Lambda-Ausdruck erstellt einen Variablenbereich, wie es die defAnweisung tun würde, aber in Lambda sind keine Anweisungen zulässig. Zuweisung als Anweisung in Python bedeutet, dass keine Variablenzuweisungen in Lambda zulässig sind, aber ein Listenverständnis ein Ausdruck ist ...

Dieses Verhalten wurde in Python 3 behoben - keine Verständnisausdrücke oder Generatoren lecken Variablen.


Das Globale bedeutet wirklich den Modulumfang; Das Hauptmodul von Python ist das __main__; Auf alle importierten Module kann über die sys.modulesVariable zugegriffen werden. um Zugang zu erhalten __main__, kann man verwenden sys.modules['__main__'], oder import __main__; Es ist durchaus akzeptabel, dort auf Attribute zuzugreifen und diese zuzuweisen. Sie werden als Variablen im globalen Bereich des Hauptmoduls angezeigt.


Wenn ein Name jemals im aktuellen Bereich zugewiesen wird (außer im Klassenbereich), wird er als zu diesem Bereich gehörend betrachtet, andernfalls wird er als zu einem umschließenden Bereich gehörend betrachtet, der der Variablen zugewiesen wird (möglicherweise wird er nicht zugewiesen noch oder gar nicht) oder schließlich den globalen Geltungsbereich. Wenn die Variable als lokal betrachtet wird, aber noch nicht festgelegt ist oder gelöscht wurde, führt das Lesen des Variablenwerts zu UnboundLocalErroreiner Unterklasse von 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()

Der Bereich kann deklarieren, dass er die globale Variable (Modulbereich) explizit mit dem globalen Schlüsselwort ändern möchte:

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

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

Dies ist auch dann möglich, wenn es im umschließenden Bereich beschattet wurde:

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

In Python 2 gibt es keine einfache Möglichkeit, den Wert im umschließenden Bereich zu ändern. In der Regel wird dies durch einen veränderlichen Wert simuliert, z. B. eine Liste mit einer Länge von 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

In Python 3 nonlocalkommt das jedoch zur Rettung:

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.

Die nonlocalDokumentation sagt das

In einer nichtlokalen Anweisung aufgeführte Namen müssen sich im Gegensatz zu den in einer globalen Anweisung aufgeführten Namen auf bereits vorhandene Bindungen in einem umschließenden Bereich beziehen (der Bereich, in dem eine neue Bindung erstellt werden soll, kann nicht eindeutig bestimmt werden).

dh nonlocalbezieht sich immer auf den innersten äußeren nicht globalen Bereich, an den der Name gebunden wurde (dh zugewiesen, einschließlich als forZielvariable, in der withKlausel oder als Funktionsparameter verwendet).


Jede Variable, die nicht als lokal für den aktuellen Bereich oder einen umschließenden Bereich angesehen wird, ist eine globale Variable. Ein globaler Name wird im globalen Wörterbuch des Moduls nachgeschlagen. Wenn es nicht gefunden wird, wird das Global vom eingebauten Modul nachgeschlagen. Der Name des Moduls wurde von Python 2 in Python 3 geändert. in Python 2 war es __builtin__und in Python 3 heißt es jetzt builtins. Wenn Sie einem Attribut des eingebauten Moduls zuweisen, wird es anschließend für jedes Modul als lesbare globale Variable angezeigt, es sei denn, dieses Modul schattiert sie mit einer eigenen globalen Variablen mit demselben Namen.


Das Lesen des eingebauten Moduls kann ebenfalls nützlich sein. Angenommen, Sie möchten die Druckfunktion im Python 3-Stil in einigen Teilen der Datei verwenden, in anderen Teilen der Datei wird die printAnweisung jedoch weiterhin verwendet . In Python 2.6-2.7 können Sie die Python 3- printFunktion mit folgenden Funktionen abrufen :

import __builtin__

print3 = __builtin__.__dict__['print']

Das from __future__ import print_functionimportiert die printFunktion eigentlich nirgendwo in Python 2 - stattdessen deaktiviert es nur die Parsing-Regeln für printAnweisungen im aktuellen Modul, behandelt sie printwie jede andere Variablenkennung und ermöglicht so printdas Nachschlagen der Funktion in den integrierten Funktionen.

Antti Haapala
quelle
23

Die Gültigkeitsbereichsregeln für Python 2.x wurden bereits in anderen Antworten beschrieben. Das einzige, was ich hinzufügen möchte, ist, dass es in Python 3.0 auch das Konzept eines nicht lokalen Bereichs gibt (angezeigt durch das Schlüsselwort 'nonlocal'). Auf diese Weise können Sie direkt auf äußere Bereiche zugreifen und einige nützliche Tricks ausführen, einschließlich lexikalischer Verschlüsse (ohne hässliche Hacks mit veränderlichen Objekten).

EDIT: Hier ist der PEP mit mehr Informationen dazu.

Jeremy Cantrell
quelle
23

Ein etwas vollständigeres Beispiel für den Umfang:

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

Ausgabe:

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
quelle
6
Das ist eine großartige Antwort. Ich denke jedoch, dass die Unterschiede zwischen methodund hervorgehoben werden method_local_refsollten. methodkann auf die globale Variable zugreifen und sie wie in drucken 5. Global x. Aber method_local_refkann nicht , weil später definiert sie eine lokale Variable mit dem gleichen Namen. Sie können dies testen, indem Sie die x = 200Linie entfernen und den Unterschied sehen
kiril
@brianray: Was ist mit z?
Malik A. Rumi
@ Kiril Ich habe eine Notiz darüber hinzugefügt
Brianray
@ MalikA.Rumi Ich entfernte z, da es nicht interessant war
Brianray
Überraschenderweise ist dies die einzige klare Erklärung für Python-Bereiche, die ich auf allen SO finden konnte. Einfach anhand eines sehr einfachen Beispiels. Vielen Dank!
not2qubit
13

Python löst Ihre Variablen mit - im Allgemeinen - drei verfügbaren Namespaces auf.

Während der Ausführung gibt es zu jeder Zeit mindestens drei verschachtelte Bereiche, auf deren Namespaces direkt zugegriffen werden kann: Der innerste Bereich, der zuerst durchsucht wird, enthält die lokalen Namen. die Namespaces aller umschließenden Funktionen, die beginnend mit dem nächstgelegenen umschließenden Bereich durchsucht werden; Der als nächstes gesuchte mittlere Bereich enthält die globalen Namen des aktuellen Moduls. und der äußerste Bereich (zuletzt gesucht) ist der Namespace, der integrierte Namen enthält.

Es gibt zwei Funktionen: globalsund localsdie Ihnen den Inhalt von zwei dieser Namespaces anzeigen.

Namespaces werden durch Pakete, Module, Klassen, Objektkonstruktionen und Funktionen erstellt. Es gibt keine anderen Arten von Namespaces.

In diesem Fall muss der Aufruf einer Funktion mit dem Namen xim lokalen Namensraum oder im globalen Namensraum aufgelöst werden.

Lokal ist in diesem Fall der Hauptteil der Methodenfunktion Foo.spam.

Global ist - na ja - global.

Die Regel besteht darin, die verschachtelten lokalen Bereiche zu durchsuchen, die durch Methodenfunktionen (und verschachtelte Funktionsdefinitionen) erstellt wurden, und dann global zu suchen. Das ist es.

Es gibt keine anderen Bereiche. Die forAnweisung (und andere zusammengesetzte Anweisungen wie ifund try) erstellen keine neuen verschachtelten Bereiche. Nur Definitionen (Pakete, Module, Funktionen, Klassen und Objektinstanzen.)

Innerhalb einer Klassendefinition sind die Namen Teil des Klassennamensraums. code2muss beispielsweise durch den Klassennamen qualifiziert sein. Im Allgemeinen Foo.code2. Jedoch,self.code2 auch, da Python-Objekte die enthaltende Klasse als Ersatz betrachten.

Ein Objekt (eine Instanz einer Klasse) hat Instanzvariablen. Diese Namen befinden sich im Namespace des Objekts. Sie müssen vom Objekt qualifiziert werden. (variable.instance .)

Innerhalb einer Klassenmethode haben Sie Einheimische und Globale. Sie sagen self.variable, Sie sollen die Instanz als Namespace auswählen. Das wirst du bemerkenself ein Argument für jede Klassenmitgliedsfunktion ist und sie Teil des lokalen Namespace macht.

Siehe Python-Bereichsregeln , Python-Bereich , Variabler Bereich .

S.Lott
quelle
5
Dies ist veraltet. Seit 2.1 (vor 7 Jahren) gibt es mehr als zwei Bereiche, da verschachtelte Funktionen neue Bereiche einführen, sodass eine Funktion innerhalb einer Funktion Zugriff auf ihren lokalen Bereich, den Bereich der umschließenden Funktionen und den globalen Bereich (auch integrierte) hat.
Brian
Es tut mir leid, das ist nicht mehr der Fall. Python has two namespaces available. Global and local-to-something.
Rizwan Kassim
9

Wo ist x gefunden?

x wird nicht gefunden, da Sie es nicht definiert haben. :-) Es könnte in Code1 (global) oder Code3 (lokal) gefunden werden, wenn Sie es dort ablegen.

Code2 (Klassenmitglieder) ist für Code innerhalb von Methoden derselben Klasse nicht sichtbar. Normalerweise greifen Sie mit self auf sie zu. Code4 / Code5 (Schleifen) befinden sich im selben Bereich wie Code3. Wenn Sie also dort in x schreiben, ändern Sie die in Code3 definierte x-Instanz und erstellen kein neues x.

Python hat einen statischen Gültigkeitsbereich. Wenn Sie also "Spam" an eine andere Funktion übergeben, hat Spam weiterhin Zugriff auf Globals in dem Modul, aus dem es stammt (definiert in Code1), und auf alle anderen enthaltenen Bereiche (siehe unten). Auf Code2-Mitglieder würde wieder über self zugegriffen.

Lambda ist nicht anders als def. Wenn in einer Funktion ein Lambda verwendet wird, entspricht dies dem Definieren einer verschachtelten Funktion. Ab Python 2.2 sind verschachtelte Bereiche verfügbar. In diesem Fall können Sie x auf jeder Ebene der Funktionsverschachtelung binden, und Python übernimmt die innerste Instanz:

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

2 0

fun3 sieht die Instanz x aus dem nächsten enthaltenden Bereich, der dem mit fun2 zugeordneten Funktionsbereich ist. Die anderen x-Instanzen, die in fun1 und global definiert sind, sind jedoch nicht betroffen.

Vor nested_scopes - in Python vor 2.1 und in 2.1, sofern Sie nicht ausdrücklich mit einem From-Future-Import nach der Funktion fragen - sind die Bereiche von fun1 und fun2 für fun3 nicht sichtbar, sodass die Antwort von S.Lott gilt und Sie das globale x erhalten ::

0 0
Bobince
quelle
1

In Python

Jede Variable, der ein Wert zugewiesen wird, ist lokal für den Block, in dem die Zuweisung angezeigt wird.

Wenn eine Variable im aktuellen Bereich nicht gefunden werden kann, lesen Sie bitte die LEGB-Reihenfolge.

GraceMeng
quelle