问题
I came across bizarre eval behavior in Python 3 - local variables aren't picked up when eval is called in a list comprehension.
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())
It errors 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
And it works fine in Python 2:
▶ python --version
Python 2.7.8
▶ python eval.py
[0.5, 0.25, 0.75]
Moving it outside of the list comprehension removes the problem.
def apply_op():
x, y, z = [0.5, 0.25, 0.75]
return [eval("x"), eval("y"), eval("z")]
Is this intended behavior, or is it a bug?
回答1:
There is a closed issue in the bug tracker for this: Issue 5242.
The resolution for this bug is won't fix.
Some comments from the Issue read:
This is expected, and won't easily fix. The reason is that list comprehensions in 3.x use a function namespace "under the hood" (in 2.x, they were implemented like a simple for loop). Because inner functions need to know what names to get from what enclosing namespace, the names referenced in eval() can't come from enclosing functions. They must either be locals or globals.
eval() is probably already an hack, there's no need to add another hack to make it work. It's better to just get rid of eval() and find a better way to do what you want to do.
回答2:
If you want:
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())
to work you'll need to capture the locals and globals as the issue is that eval(o)
is the same has eval(o, globals(), locals())
but as the eval
appears within the generator function the results of those functions aren't the same as they were when the eval
didn't have a wrapping function so capture them outside the generator and use them inside:
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())
Or better as x,y,z
are locals and the strings are only variable names:
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())
来源:https://stackoverflow.com/questions/29336616/eval-scope-in-python-2-vs-3