2017-02-25 4 views
7
def fun(): 
    if False: 
     x=3 
    print(locals()) 
    print(x) 
fun() 

Ausgabe und Fehlermeldung kompiliert:Python lokale Variable Prinzip

{} 
--------------------------------------------------------------------------- 
UnboundLocalError       Traceback (most recent call last) 
<ipython-input-57-d9deb3063ae1> in <module>() 
     4  print(locals()) 
     5  print(x) 
----> 6 fun() 

<ipython-input-57-d9deb3063ae1> in fun() 
     3   x=3 
     4  print(locals()) 
----> 5  print(x) 
     6 fun() 

UnboundLocalError: local variable 'x' referenced before assignment 

Ich frage mich, wie der Python-Interpreter arbeitet. Beachten Sie, dass x = 3 überhaupt nicht ausgeführt wird und nicht als lokale Variable behandelt werden sollte, was bedeutet, dass der Fehler "name 'x' ist nicht definiert" ist. Aber schauen Sie in den Code und die Fehlermeldung, ist es nicht der Fall. Kann jemand hinter dieser Situation das Mechanismusprinzip der Kompilierung des Python-Interpreters erklären?

+0

möglich duplizieren http://stackoverflow.com/q/7969949/3758972 –

+0

Dies könnte relevant sein: [Kurze Beschreibung der Scoping-Regeln] (http://stackoverflow.com/questions/291978/short-description-of- Scoping-Regeln) –

+0

Wenn eine der folgenden Antworten Ihr Problem behebt, sollten Sie es akzeptieren (klicken Sie auf das Häkchen neben der entsprechenden Antwort). Das macht zwei Dinge. Es lässt jeden wissen, dass Ihr Problem zu Ihrer Zufriedenheit gelöst wurde, und es gibt der Person, die Ihnen hilft, die Unterstützung zuzuschreiben. Eine vollständige Erklärung finden Sie [hier] (http://meta.stackexchange.com/a/5235). –

Antwort

4

Also, Python wird immer jeden Namen in jeder Funktion kategorisiert als einen der lokale sein, nicht lokalen oder globale. Diese Namensbereiche sind exklusiv; Innerhalb jeder Funktion (Namen in verschachtelten Funktionen haben ihren eigenen Namensumfang) kann jeder Name nur zu einer dieser Kategorien gehören.

Wenn Python diesen Code kompiliert:

FunctionDef(
    name='fun', 
    args=arguments(...), b 
    body=[ 
     If(test=NameConstant(value=False), 
      body=[ 
       Assign(targets=[Name(id='x', ctx=Store())], value=Num(n=3)) 
      ], 
      orelse=[]) 
    ] 
) 

(einige Sachen weggelassen der Kürze halber):

def fun(): 
    if False: 
     x=3 

es in einer abstrakten Syntaxbaum als in Folge. Wenn dieser abstrakte Syntaxbaum nun in Code kompiliert wird, durchsucht Python alle Namenknoten. Wenn es irgendwelche Name Knoten, mit ctx=Store(), wird dieser Name als lokale dem umschließenden FunctionDef wenn überhaupt, sein, es sei denn außer Kraft gesetzt mit global (d global x) oder nonlocal (nonlocal x) Anweisung innerhalb der gleichen Funktionsdefinition. Die ctx=Store() wird hauptsächlich auftreten, wenn der betreffende Name auf der linken Seite einer Zuweisung oder als Iterationsvariable in einer for-Schleife verwendet wird.

Nun, wenn Python kompiliert dies zu Bytecode, der resultierende Bytecode ist

>>> dis.dis(fun) 
    4   0 LOAD_GLOBAL    0 (print) 
       3 LOAD_FAST    0 (x) 
       6 CALL_FUNCTION   1 (1 positional, 0 keyword pair) 
       9 POP_TOP 
      10 LOAD_CONST    0 (None) 
      13 RETURN_VALUE 

Der Optimierer entfernt, um die if Anweisung vollständig; Da die Variable jedoch bereits als lokal für die Funktion markiert wurde, wird LOAD_FAST für x verwendet, was dazu führt, dass x nur von lokalen Variablen und nur lokalen Variablen zugegriffen wird. Da x nicht festgelegt wurde, wird UnboundLocalError ausgelöst. Der Name print wurde dagegen nie zugewiesen und wird daher in dieser Funktion als globaler Name betrachtet. Daher wird sein Wert mit LOAD_GLOBAL geladen.

3

Die Tatsache, dass x = 3 unerreichbar ist, ist irrelevant. Die Funktion weist es zu, also muss es ein lokaler Name sein.

Bedenken Sie, dass die gesamte Datei vor Beginn der Ausführung kompiliert wird, dass die Funktion jedoch während der Ausführungsphase definiert wird, wenn der kompilierte Funktionsdefinitionsblock ausgeführt wird und das Funktionsobjekt erstellt wird.

Ein ausgefeilter Optimierer könnte unerreichbaren Code eliminieren, aber CPythons Optimierer ist nicht so intelligent - er führt nur eine sehr einfache Schlüssellochoptimierung durch.

Um sich die Python-Interna genauer anzusehen, werfen Sie einen Blick auf die Module ast und dis.

+0

können Sie bitte ein wenig mehr erarbeiten. –

+0

'Die Tatsache, dass x = 3 unerreichbar ist, ist irrelevant. Die Funktion weist ihr einen Namen zu, also muss es ein lokaler Name sein. Sie sagen also, dass "localhips()" nicht x anzeigt, weil es nur ein definierter Name ist und keinem Wert zugewiesen ist? (oder so ähnlich) –

+0

@vikash Weil die Variable nicht existiert, bis sie erstellt wird. Und da die Aufgabe nie ausgeführt wird, passiert das nie. –

4

Ein in einer Funktion verwendeter Name kann nur einen Bereich für den gesamten Funktionskörper haben. Der Umfang wird zur Kompilierzeit bestimmt (nicht wenn die Funktion ausgeführt wird).

Wenn an einer beliebigen Stelle in der Funktion eine Zuweisung zugewiesen ist (unabhängig davon, ob sie beim Aufruf der Funktion ausgeführt wird), behandelt der Compiler diesen Namen standardmäßig als lokal für die Funktion. Sie können die Anweisungen global und nonlocal verwenden, um explizit anzugeben, dass ein anderer Bereich verwendet werden soll.

Ein Sonderfall ist, wo ein Name im Körper einer Funktion zugewiesen wird und von einer anderen Funktion, die in der ersten Funktion definiert ist, zugegriffen wird. Solch eine Variable wird in eine spezielle closure Zelle gesetzt, die zwischen den Funktionen geteilt wird. Die äußere Funktion behandelt die Variable wie eine lokale, während die innere Funktion sie nur zuweisen kann, wenn sie eine nonlocal-Anweisung für den Namen enthält. Hier ist ein Beispiel eines Verschlusses und eine nonlocal Aussage:

def counter(): 
    val = 0 
    def helper(): 
     nonlocal val 
     val += 1 
     return val 
    return helper 

Neben dem Problem Sie sehen, gibt es eine andere Art von Rahmen Verwirrung, die Sie sehen können:

x = 1 
def foo(): 
    print(x) # you might expect this to print the global x, but it raises an exception 
    x = 2  # this assignment makes the compiler treat the name x as local to the function 

In der foo Funktion Der Name x wird überall als lokal betrachtet, obwohl der Aufruf print versucht, ihn zu verwenden, bevor er im lokalen Namespace zugewiesen wurde.

+1

Danke für die Erwähnung von Verschlüssen. –