为什么Python' exec'表现得很奇怪?

时间:2017-12-14 08:07:58

标签: python python-3.x python-2.7 exec

为什么以下代码在代码断开后会起作用?

我不确定如何用英语表达我的问题,所以我附上了我能提出的最小的代码来突出我的问题。

(上下文:我正在尝试为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 个答案:

答案 0 :(得分: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)