Python-Geltungsbereich von Variablen-Fehler
Der folgende code funktioniert wie erwartet, sowohl Python 2.5 und 3.0:
a, b, c = (1, 2, 3)
print(a, b, c)
def test():
print(a)
print(b)
print(c) # (A)
#c+=1 # (B)
test()
Allerdings, wenn ich die Auskommentierung Zeile (B), bekomme ich eine UnboundLocalError: 'c' not assigned
in Zeile (A). Die Werte der a
und b
sind korrekt gedruckt. Das hat mich völlig verblüfft aus zwei Gründen:
- Warum gibt es einen runtime-error geworfen in Zeile (A), weil eine spätere Anweisung in Zeile (B)?
- Warum sind Variablen, die
a
undb
gedruckt, wie erwartet, währendc
löst einen Fehler aus?
Die einzige Erklärung, die ich mit oben kommen kann ist, dass ein lokalen variable c
entsteht durch die Zuordnung c+=1
, die den Vorrang über die "Globale" variable c
noch bevor die lokale variable erstellt. Natürlich ist es nicht sinnvoll, eine variable zu "stehlen" Bereich, bevor es existiert.
Könnte jemand bitte erklären Sie dieses Verhalten?
InformationsquelleAutor tba | 2008-12-16
Du musst angemeldet sein, um einen Kommentar abzugeben.
Python behandelt Variablen in Funktionen unterschiedlich je nachdem, ob Sie Werte zuweisen, um Sie innerhalb der Funktion oder nicht. Wenn eine Funktion enthält alle Zuweisungen zu einer variable behandelt wird per default als lokale Variablen. Daher, wenn Sie kommentieren Sie die Zeile, die Sie versuchen, eine lokale variable, bevor irgendein Wert zugewiesen wurde.
Wenn Sie möchten, dass die variable
c
zu finden, um die Globalec
setzenals erste Zeile der Funktion.
Als für python 3 gibt es jetzt
die Sie verwenden können, finden Sie auf den nächsten umschließenden Funktions-scope, der eine
c
variable.Der Geltungsbereich der Variablen Entscheidung erfolgt durch den compiler, die in der Regel einmal ausgeführt, wenn Sie zuerst das Programm starten. Aber es lohnt sich, wenn man bedenkt, dass der compiler kann auch später ausgeführt werden, wenn Sie "eval" oder "exec" - Anweisungen in Ihrem Programm.
Okay danke. Ich denke, "interpretierte Sprache" bedeutet nicht ganz so viel, wie ich gedacht hatte.
Ah, die 'globalen' Schlüsselwort war genau das, was ich suchte, es schien Python fehlte. Vermutlich dieses 'Kaskaden' durch jede einschließenden Bereich, der importiert die Variablen, die mit diesem Schlagwort?
es ist am einfachsten zu verstehen, wenn Sie machen die Unterscheidung zwischen "suchen" und "zuweisen" eine variable. Lookup fällt zurück auf einen höheren Bereich, wenn der name nicht im aktuellen Umfang. Die Zuordnung erfolgt immer im lokalen Bereich (es sei denn, Sie verwenden
global
odernonlocal
zu zwingen, global oder nichtlokale Zuordnung)InformationsquelleAutor recursive
Python ist ein wenig seltsam, dass es hält alles, was in einem Wörterbuch für die verschiedenen Bereiche. Die ursprünglichen a,b,c sind in den obersten Bereich und damit in das oberste dictionary. Die Funktion hat Ihr eigenes Wörterbuch. Wenn Sie erreichen die
print(a)
undprint(b)
Aussagen, es gibt nichts mit diesem Namen im Wörterbuch, so Python sucht in der Liste nach oben und findet Sie in den globalen Wörterbuch.Nun kommen wir zum
c+=1
, die, natürlich, entsprichtc=c+1
. Wenn Python durchsucht, die Zeile heißt es "aha, es gibt eine variable mit dem Namen c, ich werde es in meinem lokalen Bereich Wörterbuch." Dann wenn es geht auf der Suche für einen Wert für c, für die c auf der rechten Seite der Zuweisung, findet er seine lokale variable mit dem Namen c, die keinen Wert hat, noch, und so den Fehler auslöst.Die Aussage
global c
oben erwähnt einfach sagt der parser, der es nutzt diec
aus dem globalen Bereich und so muss nicht eine neue.Der Grund, warum es sagt, es ist eine Frage, auf die Linie es nicht, weil es ist effektiv auf der Suche nach den Namen, bevor es versucht, code zu erzeugen, und so in gewissem Sinn denkt nicht, dass es auch wirklich zu tun, dass die Linie noch. Ich würde behaupten, dass ist ein usability-Fehler, aber es ist generell eine gute Praxis, nur lernen, nicht zu einer compiler-Meldungen zu ernst.
Wenn es jeder Komfort, verbrachte ich wohl einen Tag zu Graben und zu Experimentieren, mit diesem gleichen Problem, bevor ich etwas gefunden, Guido geschrieben hatte, über die Wörterbücher, die Alles Erklärt.
Update, siehe Kommentare:
Es nicht, Scannen Sie den code doppelt, aber es funktioniert Scannen Sie den code in zwei Phasen, sind das sogenannte Lexing und parsing.
Überlegen, wie das analysieren dieser code-Zeile funktioniert. Der lexer liest den Quelltext ein und bricht Sie in Lexeme, die "kleinsten Bestandteile" der Grammatik. Also, wenn es Sie trifft die Linie
bricht es up in so etwas wie
Der parser irgendwann machen will, diese in einen parse-Baum und führen Sie es aus, aber da es eine Zuordnung, bevor Sie es tut, sieht es für die Namen c, in der lokalen Wörterbuch, nicht sehen es, und fügt es in das Wörterbuch, die ihn als nicht initialisiert. In einer voll-kompilierten Sprache, es würde nur gehen, in der symbol-Tabelle und warten Sie für das analysieren, aber da Sie NICHT den Luxus haben, einen zweiten pass, der lexer muss ein wenig zusätzliche Arbeit, um das Leben später einfacher. Nur, dann sieht der BETREIBER, der sieht, dass die Regeln sagen: "wenn du einen operator += die linke Seite muss initialisiert wurden" und sagt: "Hoppla!"
Der Punkt hier ist, dass es noch nicht wirklich begonnen, das Parsen der Zeile noch. Das alles geschieht sozusagen in Vorbereitung auf das eigentliche Parsen, also die line counter hat sich nicht erweitert, um die nächste Zeile. Also, wenn es signalisiert den Fehler, es immer noch denkt, dass Ihr auf der vorherigen Zeile.
Wie gesagt, man könnte argumentieren, es ist ein usability-Fehler, aber es ist eigentlich eine ziemlich gewöhnliche Sache. Einige Compiler sind mehr ehrlich sein und sagen, "Fehler auf oder um die Zeile XXX", aber dieses nicht.
Nein, es ist auf der expression-Ebene. Ich werde hinzufügen, um die Antwort, ich glaube nicht, dass ich fit dieser in einem Kommentar.
Nette Antwort und btw +1 für dein Profil Kommentar, mister Martin! made my day 😀
Hinweis auf die Implementierungsdetails: In CPython, der örtliche Geltungsbereich ist in der Regel nicht behandelt, wie ein
dict
, es ist intern nur ein array (locals()
füllen Sie eindict
zurück, aber er ändert es nicht schaffen, neuelocals
). Der parse-phase wird die Suche nach jeder Zuordnung zu einer lokalen und Umwandlung von Namen, position im array und die Verwendung dieser position, Wann immer der name referenziert wird. Auf Eintrag, um die Funktion, nicht-argument einheimischen sind initialisiert, ein Platzhalter, undUnboundLocalError
s passieren, wenn eine variable gelesen wird, und seine zugehörige index hat immer noch den Platzhalter-Wert.InformationsquelleAutor Charlie Martin
Sich ein Blick auf die Demontage klären können, was passiert ist:
Wie Sie sehen können, die bytecode für den Zugriff auf ein ist
LOAD_FAST
, und für bLOAD_GLOBAL
. Dies ist, weil der compiler hat festgestellt, dass eine zugewiesen wird, innerhalb der Funktion, und klassifiziert es als eine lokale variable. Die access-Mechanismus für die einheimischen ist grundlegend für die globals - Sie sind statisch zugewiesenen Versatz der frame-Variablen-Tabelle, was bedeutet, lookup ist eine schnelle index, eher als die teurere dict-lookup für globals. Weil dieses, Python ist das Lesen derprint a
Linie als "den Wert der lokalen Variablen 'a' statt in slot 0, und drucken Sie es", und wenn es erkennt, dass diese variable noch uninitialised, wirft eine exception.Dito. Ich habe zu sehen, dass.
InformationsquelleAutor Brian
Python hat eher Interessantes Verhalten, wenn Sie versuchen, traditionelle globalen Variablen Semantik. Ich erinnere mich nicht die details, aber Lesen Sie den Wert einer variable deklariert im "globalen" Rahmen ganz gut, aber wenn Sie wollen, um es zu ändern, müssen Sie die
global
Schlüsselwort. Versuchen Sietest()
:Auch der Grund, warum Sie diese Fehlermeldung bekommen, weil Sie können auch deklarieren Sie eine neue variable innerhalb der Funktion mit dem gleichen Namen wie eine 'Globale', und es wäre völlig getrennt. Der Dolmetscher denkt, dass Sie versuchen, um eine neue variable in diesem scope genannt
c
und ändern Sie es alle in einem Betrieb, das ist nicht erlaubt in Python, weil diese neuec
war nicht initialisiert.Python wird zu Lesen, zu analysieren und schalten Sie die ganze Funktion in internen bytecode, bevor er beginnt die Ausführung des Programms, so dass die Tatsache, dass der "turn c lokale variable" passiert, textlich nach dem Druck wird der Wert nicht, wie es war, egal.
InformationsquelleAutor Mongoose
Das beste Beispiel dafür, dass macht es klar ist:
beim Aufruf
foo()
dies auch wirftUnboundLocalError
zwar werden wir nie erreichen, um line -bar=0
, so logisch lokalen Variablen sollte nie geschaffen werden.Das Geheimnis liegt im "Python ist eine Interpretierte Sprache" und die Erklärung der Funktion
foo
interpretiert wird wie eine einzelne Anweisung (D. H. eine zusammengesetzte Anweisung), es ist nur interpretiert, es dumbly und schafft lokale und Globale Bereiche. Sobar
erkannt wird im lokalen Bereich vor der Ausführung.Für weitere Beispiele wie diese, die diesen post Lesen: http://blog.amir.rachum.com/blog/2013/07/09/python-common-newbie-mistakes-part-2/
Dieser Beitrag enthält eine Vollständige Beschreibung und Analysen der Python Scoping von Variablen:
InformationsquelleAutor Sahil kalra
Hier sind zwei links, die helfen können,
1: docs.python.org/3.1/faq/programming.html?highlight=nonlocal#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value
2: docs.python.org/3.1/faq/programming.html?highlight=nonlocal#how-do-i-write-a-function-with-output-parameters-call-by-reference
link beschreibt den Fehler UnboundLocalError. Link zwei können helfen, mit re-schreiben von test-Funktion. Basierend auf dem link zwei, das ursprüngliche problem könnte wie folgt umgeschrieben werden:
InformationsquelleAutor mcdon
Dies ist keine direkte Antwort auf deine Frage, aber es ist eng, es ist ein anderes Problem, verursacht durch die Beziehung zwischen augmented Zuordnung und Funktion Bereiche.
In den meisten Fällen, Sie neigen zu denken, augmented Zuordnung (
a += b
) wie genau entspricht die einfache Zuweisung (a = a + b
). Es ist möglich, um in einige Schwierigkeiten mit diesem aber, in einer Ecke der Fall. Lassen Sie mich erklären:Den Weg Python ist eine einfache Zuweisung funktioniert bedeutet, dass, wenn
a
ist, die einer Funktion übergeben (wiefunc(a)
; beachten Sie, dass Python immer pass-by-reference), danna = a + b
nicht änderna
übergeben wird. Stattdessen, es wird ändern Sie einfach die lokalen Zeiger aufa
.Aber wenn Sie
a += b
ist, dann ist es manchmal umgesetzt:oder manchmal (wenn die Methode vorhanden ist):
Im ersten Fall (solange
a
ist nicht global deklariert), gibt es keine Nebenwirkungen, die außerhalb der lokalen Bereich, wie die Zuordnung zua
nur einen Zeiger zu aktualisieren.Im zweiten Fall
a
tatsächlich ändern sich, so dass alle Verweise aufa
auf die modifizierte version. Dies ist durch den folgenden code dargestellt:Also der trick ist zu vermeiden, augmented-Zuordnung, die auf Funktion Argumente (ich versuche, nur nutzen es für lokale/loop-Variablen). Verwenden Sie einfache Aufgabe, und Sie werden sicher von zweideutigen Verhalten.
InformationsquelleAutor alsuren
Den Python-interpreter gelesen wird, eine Funktion als eine komplette Einheit. Ich denke, dass es Lesen, es in zwei Durchgängen, einmal, um sich zu sammeln Verschluss (lokale Variablen), dann wieder an schalten Sie es in byte-code.
Da bin ich mir sicher, dass Sie bereits wissen, alle Namen auf der linken Seite des '=' ist implizit eine lokale variable. Mehr als einmal habe ich dabei ertappt worden, durch änderung einer variable zugreifen zu a += und es ist plötzlich eine andere variable.
Wollte ich auch hinweisen, es ist nicht wirklich etwas zu tun mit dem globalen Bereich speziell. Sie bekommen das gleiche Verhalten mit verschachtelten Funktionen.
InformationsquelleAutor James Hopkin
c+=1
weistc
, python übernimmt die zugewiesenen Variablen sind lokal, aber in diesem Fall wurde es nicht lokal deklariert.Entweder die
global
odernonlocal
keywords.nonlocal
funktioniert nur mit python 3, so dass, wenn Sie mit python 2 und nicht wollen, um Ihre Globale variable, die Sie verwenden können, ein veränderliches Objekt:InformationsquelleAutor Colegram
Den besten Weg zum erreichen der Klasse variable ist direkt accesing durch den Klassennamen
InformationsquelleAutor Harun ERGUL
In python wir haben eine ähnliche Erklärung für alle Arten von lokalen Variablen, Klassen-Variablen und Globale Variablen. wenn Sie finden Globale variable aus Methode, python denken, dass Sie tatsächlich aufgerufene variable aus der Methode selbst, die noch nicht definiert sind und somit Fehler schmeißen.
Zu beziehen Globale variable, die wir verwenden
globals()['variableName'].
in Ihrem Fall verwenden, globals()['a],globals()['b'] und globals()['c'] anstelle von a,b und c bzw..
InformationsquelleAutor Santosh Kadam