这是因为d
没有使用exec
提供的全局变量;它使用了第一个exec
中存储引用的映射。虽然你在新字典中设置了'b'
,但你从未在那个函数的全局变量中设置b
。
>>> d={}
>>> exec('def a():b',d)
>>> exec('b=None',d)
>>> d['a'].__globals__ is d
True
>>> 'b' in d['a'].__globals__
True
vs
>>> d={}
>>> exec('def a():b',d)
>>> d = d.copy()
>>> exec('b=None',d)
>>> d['a'].__globals__ is d
False
>>> 'b' in d['a'].__globals__
False
如果
exec
不是这样工作的,那么这个也将失败:
mod.py
b = None
def d():
b
main.py
from mod import d
d()
函数会记住它最初创建时的环境。
无法更改现有函数指向的字典。您可以明确地修改其全局变量,或者可以完全创建另一个函数对象:
from types import FunctionType
def rebind_globals(func, new_globals):
f = FunctionType(
code=func.__code__,
globals=new_globals,
name=func.__name__,
argdefs=func.__defaults__,
closure=func.__closure__
)
f.__kwdefaults__ = func.__kwdefaults__
return f
def foo(a, b=1, *, c=2):
print(a, b, c, d)
new_globals = {'d': 3, '__builtins__': __builtins__}
new_foo = rebind_globals(foo, new_globals)
new_foo(a=0)
exec
和eval
的提示;-) - cs95a
之前执行定义b
的exec
,则第二个版本将起作用。或者,如果您删除第一个副本(但我猜您已经知道了)。我仍然不太确定为什么复制会搞乱东西,但我猜这与b
是全局变量有关。 - PM 2Ring