为什么Python的'exec'表现异常?

3

为什么以下代码能够正常运行,而其后的代码却不能?

我不确定如何用英语表达我的问题,因此我附上了我能想到的最小代码来突出我的问题。

(背景:我正在尝试创建一个Python终端环境,但由于某些原因命名空间似乎被搞砸了,下面的代码似乎是我的问题的本质)

没有错误:

d={}
exec('def a():b',d)
exec('b=None',d)
exec('a()',d)

错误:

d={}
exec('def a():b',d)
d=d.copy()
exec('b=None',d)
d=d.copy()
exec('a()',d)

1
这是你不要使用 execeval 的提示;-) - cs95
有点棘手。请注意,如果您在定义a之前执行定义bexec,则第二个版本将起作用。或者,如果您删除第一个副本(但我猜您已经知道了)。我仍然不太确定为什么复制会搞乱东西,但我猜这与b是全局变量有关。 - PM 2Ring
1个回答

2

这是因为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)


# add __builtins__ so that `print` is found...    
new_globals = {'d': 3, '__builtins__': __builtins__}
new_foo = rebind_globals(foo, new_globals)
new_foo(a=0)

谢谢 - 这确实有帮助!有没有办法将全局变量注入到函数中?这样我就可以以一种使b()响应的方式更改main.py中b的值。 - Ryan Burgert
1
@RyanBurgert 添加了 - Antti Haapala -- Слава Україні

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接