Python Globals, Einheimische und UnboundLocalError
-
03-07-2019 - |
Frage
Ich lief vor kurzem über diesen Fall von UnboundLocalError
, die seltsam erscheint:
import pprint
def main():
if 'pprint' in globals(): print 'pprint is in globals()'
pprint.pprint('Spam')
from pprint import pprint
pprint('Eggs')
if __name__ == '__main__': main()
Welche erzeugt:
pprint is in globals()
Traceback (most recent call last):
File "weird.py", line 9, in <module>
if __name__ == '__main__': main()
File "weird.py", line 5, in main
pprint.pprint('Spam')
UnboundLocalError: local variable 'pprint' referenced before assignment
pprint
ist eindeutig in globals
gebunden und wird sich in locals
in der folgenden Erklärung gebunden sein. Kann jemand eine Erklärung bieten, warum es hier nicht glücklich Lösung pprint
auf die Bindung in globals
ist?
Edit: Dank der guten Antworten, die ich meine Frage mit relevanten Terminologie klären können:
Beim Kompilieren der Kennung pprint
wird als lokal auf den Rahmen markiert. Hat das Ausführungsmodell keinen Unterschied hat mit innerhalb des Rahmens wird die lokale Kennung gebunden? Kann es sagen, „beziehen sich auf die globale, bis diese Bytecode-Befehl Bindung auf, an welcher Stelle es Rebound an eine lokale Bindung war“, oder ist das Ausführungsmodell für das nicht-Konto?
Lösung
Sieht aus wie Python die from pprint import pprint
Linie und Markierungen pprint
als Name lokal für main()
vor sieht beliebigen Code auszuführen. Da Python denkt pprint eine lokale Variable sein soll, ist es mit pprint.pprint()
Referenzierung bevor es mit der from..import
Anweisung „Zuweisung“, wirft es diesen Fehler.
Das ist so viel Sinn, wie ich daraus machen kann.
Die Moral, ist natürlich immer jene import
Aussagen an der Spitze des Bereichs zu setzen.
Andere Tipps
Wo ist die Überraschung? Alle Variable global zu einem Umfang, die Sie in diesem Rahmen neu zugewiesen wird lokal vom Compiler in diesen Umfang markiert.
Wenn die Einfuhren anders behandelt werden würde, , die wäre überraschend, imho.
Es kann einen Fall macht für die Module nicht nach Symbolen darin verwendeten Benennung oder umgekehrt, though.
Nun, das war interessant genug für mich ein wenig zu experimentieren und ich lese durch http: / /docs.python.org/reference/executionmodel.html
Dann einige Bastelei mit Ihrem Code hier und da getan hat, das ist, was ich finden kann:
code:
import pprint
def two():
from pprint import pprint
print globals()['pprint']
pprint('Eggs')
print globals()['pprint']
def main():
if 'pprint' in globals():
print 'pprint is in globals()'
global pprint
print globals()['pprint']
pprint.pprint('Spam')
from pprint import pprint
print globals()['pprint']
pprint('Eggs')
def three():
print globals()['pprint']
pprint.pprint('Spam')
if __name__ == '__main__':
two()
print('\n')
three()
print('\n')
main()
Ausgabe:
<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
'Eggs'
<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
'Spam'
pprint is in globals()
<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
'Spam'
<function pprint at 0xb7d596f4>
'Eggs'
Bei dem Verfahren two()
from pprint import pprint
aber überschreibt nicht den Namen pprint
in globals
, da das global
Schlüsselwort nicht im Rahmen des two()
verwendet.
In Verfahren three()
da es keine Erklärung von pprint
Namen in lokalen Bereich wird standardmäßig die globalen Namen pprint
, die ein Modul
Während in main()
, zuerst das Stichwort global
verwendet wird, , um alle Verweise in dem Anwendungsbereich der Methode pprint
main()
zum global
Namen pprint
beziehen. Die, wie wir ein Modul zunächst sehen können und in dem global
namespace
mit einem Verfahren außer Kraft gesetzt, wie wir die from pprint import pprint
Obwohl dies nicht die Frage als solche zu beantworten, aber dennoch seine einige interessante Tatsache, denke ich.
=====================
Bearbeiten Eine weitere interessante Sache.
Wenn Sie ein Modul haben sagen:
mod1
from datetime import datetime
def foo():
print "bar"
und ein anderes Verfahren sagen:
mod2
import datetime
from mod1 import *
if __name__ == '__main__':
print datetime.datetime.now()
, die auf den ersten Blick scheinbar korrekt, da das Modul datetime
in mod2
importiert haben.
jetzt, wenn Sie versuchen, mod2 als Skript ausführen wird es einen Fehler aus:
Traceback (most recent call last):
File "mod2.py", line 5, in <module>
print datetime.datetime.now()
AttributeError: type object 'datetime.datetime' has no attribute 'datetime'
, da der zweite Import from mod2 import *
den Namen datetime
im Namensraum außer Kraft gesetzt hat, daher der erste import datetime
ist nicht mehr gültig.
Moral. So ist die Reihenfolge der Importe, die Art des Imports (von x Import *) und das Bewusstsein für die Einfuhr in importierten Module - Angelegenheiten
Diese Frage wurde vor einigen Wochen beantwortet, aber ich denke, dass ich die Antworten ein wenig klären können. Zunächst einige Fakten.
1: In Python,
import foo
ist fast genau die gleiche wie
foo = __import__("foo", globals(), locals(), [], -1)
. 2: Wenn Code in einer Funktion ausgeführt wird, wenn Python eine Variable trifft, die in der Funktion definiert wird noch nicht, es sieht im globalen Bereich
3: Python hat eine Optimierung für Funktionen verwendet, genannt „Einheimischen“. Wenn Python eine Funktion tokenizes, hält es den Überblick über alle Variablen Sie zuweisen. Er weist jede dieser Variablen eine Zahl von einem lokalen monoton steigenden integer. Wenn Python die Funktion ausgeführt wird, erstellt sie ein Array mit so vielen Schlitzen wie es lokale Variablen sind, und es weist jeder Schlitz ein besonderer Wert, der bedeutet „wurde nicht zugewiesen noch“, und das ist, in dem die Werte für diese Variablen gespeichert sind. Wenn Sie eine lokale Referenz, die nicht noch zugewiesen hat, sieht Python, dass besonderen Wert und löst eine UnboundLocalValue Ausnahme.
Die Bühne ist nun eingestellt. Ihr „von pprint Import pprint“ ist wirklich eine Form der Zuordnung. So Python erstellt eine lokale Variable mit dem Namen „pprint“, die die globale Variable verschliesst. Wenn Sie dann auf „pprint.pprint“ in der Funktion beziehen, drücken Sie den besonderen Wert und Python wirft die Ausnahme. Wenn Sie nicht, dass die Import-Anweisung in der Funktion haben, würde Python verwenden, um die normale Look-in-Einheimischen-first-then-Look-in-Globals Auflösung und die pprint Modul in Globals finden.
Um dies disambiguate Sie das „global“ Schlüsselwort verwenden können. Natürlich jetzt haben Sie bereits Vergangenheit Ihres Problems gearbeitet, und ich weiß nicht, ob Sie wirklich für erforderlich „global“ oder wurde ein anderer Ansatz genannt.