在exec中赋值的变量出现NameError,尽管它存在于locals中。
在exec中赋值的变量出现NameError,尽管它存在于locals中。
以下代码在Python2
和Python3
中输出不同结果:
from sys import version print(version) def execute(a, st): b = 42 exec("b = {}\nprint('b:', b)".format(st)) print(b) a = 1. execute(a, "1.E6*a")
Python2
输出:
2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] ('b:', 1000000.0) 1000000.0
Python3
输出:
3.2.3 (default, Apr 11 2012, 07:15:24) [MSC v.1500 32 bit (Intel)] b: 1000000.0 42
为什么Python2
将execute
函数中的变量b
绑定到exec
函数字符串中的值,而Python3
不这样做?我如何在Python3
中实现Python2
的行为?我已经尝试在Python3
中向exec
函数传递全局和本地的字典,但是到目前为止都没有起作用。
--- 编辑 ---
阅读Martijns的回答后,我进一步分析了这个问题。在以下示例中,我将locals()
字典作为d
传递给exec
,但是d['b']
打印出的结果与仅打印b
不同。
from sys import version print(version) def execute(a, st): b = 42 d = locals() exec("b = {}\nprint('b:', b)".format(st), globals(), d) print(b) # 这将打印出42 print(d['b']) # 这将打印出1000000.0 print(id(d) == id(locals())) # 这将打印出True a = 1. execute(a, "1.E6*a") 3.2.3 (default, Apr 11 2012, 07:15:24) [MSC v.1500 32 bit (Intel)] b: 1000000.0 42 1000000.0 True
比较d
和locals()
的id,显示它们是同一个对象。但在这种情况下,b
应该与d['b']
相同。我的示例中有什么问题?
在Python 2和Python 3中,exec
的用法有很大的区别。在Python 2中,将exec
视为一个函数,但实际上它是Python 2中的一个语句。由于这个区别,在Python 3中使用exec
不能改变函数作用域中的局部变量,即使在Python 2中是可以的,甚至不能改变之前声明的变量。
在Python 2中,使用exec
语句意味着编译器知道要关闭局部作用域的优化(例如,从LOAD_FAST
切换到LOAD_NAME
,以在局部和全局作用域中查找变量)。而exec()
作为一个函数,不再有这个选项,函数作用域现在始终被优化。
此外,在Python 2中,exec
语句会使用PyFrame_LocalsToFast
将locals()
中找到的所有变量显式复制回函数局部变量,但前提是没有提供globals和locals参数。
正确的解决方法是为exec()
调用使用一个新的命名空间(一个字典):
def execute(a, st): namespace = {} exec("b = {}\nprint('b:', b)".format(st), namespace) print(namespace['b'])
exec()
文档非常明确地说明了这个限制:
注意:默认的locals与函数
locals()
的描述相同:不应尝试修改默认的locals字典。如果需要在函数exec()
返回后查看代码对局部变量的影响,请传递一个显式的locals字典。
在这段对话中,还提到了一个问题,就是为什么在最后一个例子中,print(b)
输出42,而print(d['b'])
输出1000000.0,但它们的id是相等的:id(d) == id(locals())
输出True
。
其中的解释是locals()
的返回值是单向的,该映射可以被修改,但函数的真实局部变量不会受到影响。
Python 3不允许修改函数作用域内的局部变量,解决方法是使用一个新的命名空间来调用exec()
。