Python 2中的Eval范围与3

时间:2022-07-28 23:23:10

I came across bizarre eval behavior in Python 3 - local variables aren't picked up when eval is called in a list comprehension.

我在Python 3中遇到了奇怪的eval行为 - 当在列表推导中调用eval时,不会拾取局部变量。

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 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 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?

这是预期的行为,还是一个错误?

2 个解决方案

#1


25  

There is a closed issue in the bug tracker for this: Issue 5242.

错误跟踪器中存在一个已关闭的问题:问题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.

这是预期的,不会轻易修复。原因是3.x中的列表推导使用了“引擎盖下”的函数命名空间(在2.x中,它们的实现类似于简单的for循环)。因为内部函数需要知道从封闭命名空间中获取什么名称,所以eval()中引用的名称不能来自封闭函数。它们必须是本地人或全球人。

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.

eval()可能已经是一个hack,没有必要添加另一个hack来使它工作。最好摆脱eval()并找到更好的方法来做你想做的事情。

#2


4  

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:

工作你需要捕获本地和全局因为问题是eval(o)是相同的eval(o,globals(),locals())但是因为eval出现在生成器函数中的结果函数与eval没有包装函数时的函数不同,因此将它们捕获到生成器外部并在内部使用它们:

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:

或者更好,因为x,y,z是本地人,而字符串只是变量名:

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())

#1


25  

There is a closed issue in the bug tracker for this: Issue 5242.

错误跟踪器中存在一个已关闭的问题:问题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.

这是预期的,不会轻易修复。原因是3.x中的列表推导使用了“引擎盖下”的函数命名空间(在2.x中,它们的实现类似于简单的for循环)。因为内部函数需要知道从封闭命名空间中获取什么名称,所以eval()中引用的名称不能来自封闭函数。它们必须是本地人或全球人。

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.

eval()可能已经是一个hack,没有必要添加另一个hack来使它工作。最好摆脱eval()并找到更好的方法来做你想做的事情。

#2


4  

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:

工作你需要捕获本地和全局因为问题是eval(o)是相同的eval(o,globals(),locals())但是因为eval出现在生成器函数中的结果函数与eval没有包装函数时的函数不同,因此将它们捕获到生成器外部并在内部使用它们:

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:

或者更好,因为x,y,z是本地人,而字符串只是变量名:

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())