在研究这个答案时,令我惊讶的是,我发现它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
字节码更改为 a 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
但我不明白为什么。这是记录在案的行为吗?这是 cpython 的实现细节吗?(它在 IronPython 中的工作方式相同,尽管该dis
模块不起作用)