考虑以下示例:
i=7
j=8
k=10
def test():
i=1
j=2
k=3
return dict((name,eval(name)) for name in ['i','j','k'])
它返回:
>>> test()
{'i': 7, 'k': 10, 'j': 8}
为什么 eval 不考虑函数内定义的变量?根据文档,可以选择性地传递 globals 和 locals 字典。这是什么意思?最后,我该如何修改这个小例子让它能工作?
考虑以下示例:
i=7
j=8
k=10
def test():
i=1
j=2
k=3
return dict((name,eval(name)) for name in ['i','j','k'])
它返回:
>>> test()
{'i': 7, 'k': 10, 'j': 8}
为什么 eval 不考虑函数内定义的变量?根据文档,可以选择性地传递 globals 和 locals 字典。这是什么意思?最后,我该如何修改这个小例子让它能工作?
生成器被实现为函数作用域:(参见Python官方文档)
类块中定义的名称范围仅限于类块本身,不会扩展到方法的代码块中——这包括生成器表达式,因为它们是使用函数作用域实现的。
因此,在dict()
构造函数内部的生成器具有其自己的locals()
字典。现在让我们来看一下Py_eval
的源代码,特别是当globals()
和locals()
都为None
时:
if (globals == Py_None) {
globals = PyEval_GetGlobals();
if (locals == Py_None)
locals = PyEval_GetLocals();
}
所以对于你的例子,当循环执行时,PyEval_GetLocals()
将为空,而 globals()
将是全局字典。请注意,在函数内部定义的 i
、j
和k
不在生成器的本地作用域中,而是在其封闭作用域中。>>> dict((name,eval(name, globals(), {})) for name in ['i', 'j', 'k'])
{'i': 7, 'k': 10, 'j': 8}
>>> def test():
i, j, k = range(1, 4)
return dict((j, locals()) for _ in range(i))
>>> test()
{2: {'.0': <listiterator object at 0x02F50A10>, 'j': 2, '_': 0}}
在该范围内使用 j
会将其从函数绑定,因为这是最近封闭的范围,但是 i
和 k
没有被局部绑定(因为 k
没有被引用,而 i
只用于创建 range
)。
return dict(i=i, j=j, k=k)
或者使用字典字面值:
return {'i': i, 'j': j, 'k': k}
global
来修改它的工作方式,但这是一个不好的方法。另一方面,通常使用eval
也是不好的做法。 - Rusty