如何将来自多个对 exec 的调用的依赖全局变量与独立的全局变量字典合并

How to merge dependent globals from multiple calls to exec with independent globals dicts

此代码工作正常 - 它将 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 中,这些调用在最初调用时不共享相同的全局变量字典?

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

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

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

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

g2.update(g1) # instead of creating merged

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