0

此代码工作正常 - 它定义do_return为全局,它定义do_proxy为全局,并且可以在调用时do_proxy解析。do_return

g1 = {}
exec("def do_return(): return 1", g1)
exec("def do_proxy(): return do_return()", g1)
merged = {**g1}
merged.pop("__builtins__")
print(merged.keys())
exec("print(do_proxy())", merged)
$ python3 main.py 
dict_keys(['do_return', 'do_proxy'])
1

此代码不会:

g1 = {}
g2 = {}
exec("def do_return(): return 1", g1)
exec("def do_proxy(): return do_return()", g2)
merged = {**g1, **g2}
merged.pop("__builtins__")
print(merged.keys())
exec("print(do_proxy())", merged)

它抱怨do_return没有定义,即使有一个全局命名do_return

$ python3 main.py 
dict_keys(['do_return', 'do_proxy'])
Traceback (most recent call last):
  File "main.py", line 8, in <module>
    exec("print(do_proxy())", merged)
  File "<string>", line 1, in <module>
  File "<string>", line 1, in do_proxy
NameError: name 'do_return' is not defined

有没有办法合并来自多个调用的依赖全局变量,这些调用exec在最初被调用时不共享相同的全局字典?

如果我global do_return在内部调用也会发生同样的情况do_proxy(尽管如果需要,我也希望第一个示例失败,同样需要它)。

4

1 回答 1

2

当你调用一个函数时,它使用自己对定义它的全局命名空间的引用,而不是调用它的全局变量。您可以通过检查__globals__函数的属性来看到这一点。在您的代码的第一个版本中,merged['do_proxy'].__globals__是对 的引用g1,其中也包含您的do_return函数。但是在您的代码的第二个版本中,do_proxy's__globals__是对 的引用g2,它不包含其他函数。

如果你仔细想想,这是有道理的。这类似于在另一个模块中定义的函数如何在其模块的全局命名空间中查找名称,而不是在您导入它的命名空间中查找名称,并且两个不同模块中的函数只有在导入它们后传递引用时才能交互,或者如果其中一个模块导入另一个。

您可以在代码中使用一些解决方法。一种选择是将g1定义合并到g2字典中,而不是使用组合结果创建新字典。

g2.update(g1) # instead of creating merged

当然,这适用于do_proxy引用,但如果想调用(可能作为某种递归算法的一部分)do_return,它不会反向工作。您也许可以合并两种方式,但这可能会变得混乱。do_returndo_proxy

于 2020-04-15T00:32:31.370 回答