执行前模块命名空间初始化



我试图在运行时通过使用importlib.reload重新加载模块来动态更新代码。但是,在执行模块的代码之前,我需要设置一个特定的模块变量。在重新加载后,我可以很容易地将其设置为属性,但每个模块都已经执行了代码(例如,定义了默认参数)。

一个简单的例子:

# module.py
def do():
try:
print(a)
except NameError:
print('failed')
# main.py
import module
module.do() # prints failed
module.a = 'succeeded'
module.do() # prints succeeded

所需的伪代码:

import_module_without_executing_code module
module.initialise(a = 'succeeded')
module.do()

有没有一种方法可以控制模块名称空间初始化(比如使用元类的类)?

除了交互式调试之外,使用reload通常不是一个好主意。例如,它可以很容易地创建两个类型为module.A的对象不是同一类型的情况。

你想要的是execfile。传递一个globals字典(您不需要显式的locals字典)来保持每次执行的隔离;你提前存储在其中的任何东西都和你想要的"预设"变量完全一样。如果您确实想更改"真实"的模块接口,那么可以使用一个包装器模块来调用(或仅作为属性保存)更改文件中最近加载的函数。

当然,由于您使用的是Python3,因此必须使用execfile的其中一个替换项。

严格来说,我不认为有一种方法可以实现您在Python中所描述的内容。然而,假设您拥有要导入的模块,对于需要一些初始化输入的Python模块,一种常见的方法是使用init函数。

如果你只需要设置一些内部变量,比如上面例子中的a,那就很容易了:只需声明一些模块全局变量,并在init函数中设置它们:

演示:https://repl.it/MyK0

模块:

## mymodule.py
a = None
def do():
print(a)

def init(_a):
global a
a = _a

Main:

## main.py
import mymodule
mymodule.init(123)
mymodule.do()
mymodule.init('foo')
mymodule.do()

输出:

123
foo

事情可能会变得更棘手的是,如果你需要真正重新定义一些函数,因为一些动态的内部东西取决于你给出的输入。这里有一个解决方案,借鉴自https://stackoverflow.com/a/1676860.基本上,我们的想法是通过使用魔术变量__name__索引到系统模块字典sys.modules中来获取对当前模块的引用,然后定义或覆盖需要它的函数。我们可以在本地将函数定义为内部函数,然后将它们添加到模块中:

演示:https://repl.it/MyHT/2

模块:

## mymodule.py
import sys
def init(a):
current_module = sys.modules[__name__]
def _do():
try:
print(a)
except NameError:
print('failed')
current_module.do = _do

最新更新