2015-03-30 4 views
25

stieß ich auf bizarres eval Verhalten in Python 3 - lokale Variablen nicht abgeholt werden, wenn eval in einer Liste Verständnis genannt wird.Eval Umfang in Python 2 vs. 3

def apply_op(): 
    x, y, z = [0.5, 0.25, 0.75] 
    op = "x,y,z" 
    return [eval(o) for o in op.split(",")] 
print(apply_op()) 

Es Fehler in Python 3:

▶ python --version 
Python 3.4.3 
▶ python eval.py 
Traceback (most recent call last): 
    File "eval.py", line 7, in <module> 
    print(apply_op()) 
    File "eval.py", line 5, in apply_op 
    return [eval(o) % 1 for o in op.split(",")] 
    File "eval.py", line 5, in <listcomp> 
    return [eval(o) % 1 for o in op.split(",")] 
    File "<string>", line 1, in <module> 
NameError: name 'x' is not defined 

Und es funktioniert gut in Python 2:

▶ python --version 
Python 2.7.8 
▶ python eval.py 
[0.5, 0.25, 0.75] 

bewegen sie außerhalb der Liste Verständnis beseitigt das Problem.

def apply_op(): 
    x, y, z = [0.5, 0.25, 0.75] 
    return [eval("x"), eval("y"), eval("z")] 

Ist das beabsichtigte Verhalten, oder ist es ein Fehler?

+0

[hier] (http://stackoverflow.com/questions/4198906/python-list-comprehension-rebind-names-even- after-scope-of-comprehension-is-thi) gibt einige weitere Informationen über Bereiche und Listen-Comprehensions in p2 und p3. – Marcin

+3

Der Spaßfaktor ist, wenn man ihn aus dem Funktionsumfang verschiebt, wird das Problem ebenfalls gestoppt. im Zusammenhang: http://bugs.python.org/msg81898 – wim

+0

@Marcin dies ist nicht mit dem Umfang der Schleife Variable außerhalb von List Comprehensions Leaking. es geht um den Umfang der Schleifenvariablen innerhalb des Verständnisses selbst. – wim

Antwort

25

Es ist ein geschlossen Problem im Bug-Tracker dafür: Issue 5242.

Die Auflösung für diesen Fehler ist wird nicht behoben.

Einige Kommentare aus der Ausgabe lesen:

Dies ist zu erwarten, und reparieren nicht leicht wird. Der Grund ist, dass die Liste in 3.x einen Funktionsnamensraum "unter der Haube" verwendet (in 2.x, wurden sie wie eine einfache for-Schleife implementiert). Da die inneren Funktionen wissen müssen, welche Namen aus dem umschließenden Namespace abgerufen werden sollen, können die in eval() referenzierten Namen nicht aus umschließenden Funktionen stammen. Sie müssen entweder Einheimische oder Globals sein.

Eval() ist wahrscheinlich schon ein Hack, es gibt keine Notwendigkeit, einen weiteren Hack hinzuzufügen, damit es funktioniert. Es ist besser, eval() einfach loszuwerden und eine bessere Weise zu finden, um zu tun, was Sie tun möchten.

+2

Ja, ich kann das gleiche Verhalten in Python 2 mit einer expliziten inneren Funktion reproduzieren. Interessanterweise zeigen jedoch sowohl Perl als auch JavaScript, die auch innere Funktionen unterstützen, * dieses Verhalten nicht: 'function foo() {var x = 42; Funktionsleiste() {return eval ("x")}; Rücksprungleiste}; foo()() 'liefert 42 wie erwartet, so dass die Anforderung, dass" innere Funktionen wissen müssen, welche Namen aus dem umschließenden Namespace erhalten werden ", eine Eigenart von Python ist, keine generelle Einschränkung innerer Funktionen. –

+1

Es ist wirklich bedauerlich, dass jemand sagen würde "das wird erwartet". Das erwarte ich nicht. Ein Problem besteht darin, dass cpython eine anonyme Funktion erstellt, die alle lokalen Variablen des Aufrufers anzeigt. Wenn dies nicht der Fall ist, sollte die Dokumentation aktualisiert werden. –

+0

@NeilG: Wenn "eval" aufgerufen wird, ist es zu spät, Entscheidungen über die Verfügbarkeit von Schließvariablen zu treffen. Dies zu unterstützen, würde die Kompilierungszeit von "eval" erfordern, wie der Hack, der für 0-args 'super' verwendet wird, mit ähnlichen Konsequenzen, wie wenn man' superm = super' macht, kann man 0-args 'soupern' nicht verwenden . Das oder jede Funktion mit einer expliziten oder impliziten verschachtelten Funktion müsste den Closure-Variablenmechanismus für alle seine Variablen verwenden und sie allen verschachtelten Funktionen zur Verfügung stellen, wodurch ein großer Teil der Python-Variablenauflösung verlangsamt würde. – user2357112

4

Wenn Sie wollen:

def apply_op(): 
    x, y, z = [0.5, 0.25, 0.75] 
    op = "x,y,z" 
    return [eval(o) for o in op.split(",")] 
print(apply_op()) 

Sie arbeiten werden, die Einheimischen und Globals erfassen müssen, wie das Problem ist, dass eval(o) die gleiche ist eval(o, globals(), locals()) hat aber wie der eval erscheint innerhalb der Generatorfunktion der Ergebnisse dieser Funktionen sind nicht die gleichen, wie sie waren, wenn die eval kein Umwicklungsfunktion so erfassen sie außerhalb des Generators und sie hatte innen verwenden:

def apply_op(): 
    x, y, z = [0.5, 0.25, 0.75] 
    op = "x,y,z" 
    _locals = locals() 
    _globals = globals() 
    return [eval(o, _globals, _locals) for o in op.split(",")] 
print(apply_op()) 

Oder besser als x,y,z sind Einheimische und die Saiten sind nur Variablennamen:

def apply_op(): 
    x, y, z = [0.5, 0.25, 0.75] 
    op = "x,y,z" 
    _locals = locals() 
    return [_locals[o] for o in op.split(",")] 
print(apply_op())