学习中这个答案 https://stackoverflow.com/a/18647337/65696,令我惊讶的是,我发现exec
有奇怪的行为;
>>> def f1():
... return x
...
>>> def f2():
... exec ""
... return x
...
>>> f1()
Traceback (most recent call last):
...
NameError: global name 'x' is not defined
>>> f2()
Traceback (most recent call last):
...
NameError: name 'x' is not defined
>>> x = 'bar'
>>> f1()
'bar'
>>> f2()
'bar'
显然,两者都返回一些全局值x
;但如果f2()
略有改变:
>>> def f2():
... exec "x = 'im local now'"
... return x
...
>>> f2()
'im local now'
f2 返回它自己的特殊副本x
,即使 f2 的主体中似乎没有任何内容会导致这种情况(没有分配给 x)。
我可以很容易地看到这是如何发生的,exec
声明改变了LOAD_GLOBAL
字节码转换为LOAD_NAME
,与存在类似yield
将函数变成生成器。
>>> dis.dis(f1)
2 0 LOAD_GLOBAL 0 (x)
3 RETURN_VALUE
>>> dis.dis(f2)
2 0 LOAD_CONST 1 ('')
3 LOAD_CONST 0 (None)
6 DUP_TOP
7 EXEC_STMT
3 8 LOAD_NAME 0 (x)
11 RETURN_VALUE
但我不明白的是why。这是有记录的行为吗?这是cpython的实现细节吗? (它在 IronPython 中的工作方式相同,尽管dis
模块不起作用)
想象一个更一般的情况:
def f(stuff):
exec(stuff)
return x
Python显然必须使用LOAD_NAME
这里,因为它不知道代码是否在stuff
会搞乱x
。同样的情况也适用于any use of exec()
,即使参数是一个常量——Python 根本就没有进行足够深入的分析来确定任何exec()
碰巧是安全的。
(有一个很好的理由说明为什么它也不应该进行这种分析:唯一的情况是它could即使分析取得任何程度的成功也会exec()
不断地争论,这是毫无意义的。如果参数提前完全已知,它应该只是普通代码!)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)