封闭如何在runpy中工作?

时间:2011-08-17 11:21:36

标签: python closures runpy

当我尝试运行使用runpy模块加载的文件中定义的方法时出现意外行为。这些方法没有看到在该方法之外定义的任何变量(包括导入的模块)。我是这样做的:

#test.py
import runpy
env = runpy.run_path('test', {'y':'world'})
env['fn']()

#test
import re

print(re.compile(r'^hello', re.IGNORECASE).sub('', "hello world"))
x = "hello"
print(x)
print(y)

def fn():
    try:
        print(re.compile(r'^hello', re.IGNORECASE).sub('', "hello world"))
    except:
        print("No re")
    try:
        print(x)
    except:
        print("No x")
    try:
        print(y)
    except:
        print("No y")

我对test.py的预期输出是:

 world
hello
world
 world
hello
world

因为fn会形成re,x和y的闭包。

然而,我得到了:

 world
hello
world
No re
None
None

看起来re没有在fn中定义,即使它应该是正常的闭包行为。 x和y甚至更奇怪,因为它们似乎已定义但设置为None。

为什么这样以及闭包如何与runpy一起使用?如何才能实现fn可以“看到”外部变量的正常行为?

1 个答案:

答案 0 :(得分:4)

好的,这是对Python处理模块的好奇心,我知道但不完全理解。我在处理IPython时遇到过它,在a comment中对它进行了解释。

当Python运行模块时,它会生成一个模块对象,其属性是模块中的全局名称。当模块超出范围并被销毁时,这些属性将设置为None。正如您所发现的那样,在函数中定义的代码将这些视为全局变量。您可以通过将def g(): return globals()添加到您的文件,然后调用env["g"]()

来证明这一点

我不知道runpy是否有办法解决这个问题。 IPython使用一些复杂的代码来重用模块对象来运行其他文件,缓存其__dict__的副本以使其中的引用保持活动状态。如果您有兴趣,请查看magic_run function