0

考虑三个模块a.pyb.pymain.py。它们都可以位于任意位置,但为了示例的目的,它们都将位于同一目录中。

模块通过使用 指定其路径来a加载,并且对 . 执行相同的操作。bimportlib.util.spec_from_file_locationmaina

# b.py
eggs = 42
# a.py
import importlib.util
import os

B_PATH = os.path.join(os.path.dirname(__file__), 'b.py')  # could be an arbitrary path
spec = importlib.util.spec_from_file_location('b', B_PATH)
b = importlib.util.module_from_spec(spec)
spec.loader.exec_module(b)


spam = "ham"
print(f"{__name__} says {b.eggs=}")
# main.py
import importlib.util
import os

A_PATH = os.path.join(os.path.dirname(__file__), 'a.py')  # could be an arbitrary path
spec = importlib.util.spec_from_file_location('a', A_PATH)
a = importlib.util.module_from_spec(spec)
spec.loader.exec_module(a)


print(f"{__name__} says {a.spam=}")

当我们运行时main.py

$ python main.py
a says b.eggs=42
__main__ says a.spam='ham'

我该如何修补才能看到修补b.eggs后的值?从其文件位置加载并在加载之前进行修补不起作用:main.pya.pybb.eggsa

# main.py v2
import importlib.util
import os

B_PATH = os.path.join(os.path.dirname(__file__), 'b.py')
spec = importlib.util.spec_from_file_location('b', B_PATH)
b = importlib.util.module_from_spec(spec)
spec.loader.exec_module(b)

b.eggs = "patched"

A_PATH = os.path.join(os.path.dirname(__file__), 'a.py')
spec = importlib.util.spec_from_file_location('a', A_PATH)
a = importlib.util.module_from_spec(spec)
spec.loader.exec_module(a)


print(f"{__name__} says {a.spam=}")
print(f"{__name__} says {b.eggs=}")
$ python main.py
a says b.eggs=42
__main__ says a.spam='ham'
__main__ says b.eggs='patched'

如何在不修补实际源代码的情况下做到这一点b


(注意,在这个例子中,由于所有模块都在同一个目录中,从文件路径导入是愚蠢的,因为它可以用一个import语句来完成,但这只是为了做一个最小的工作示例。在我的真实情况下,我的模块在不同的目录中,我无法修改PYTHONPATH环境变量,所以我这样做。)

4

1 回答 1

0

如图所示从源导入模块不会检查或更新sys.modules,因为它不使用__import__. 语言参考说:

直接调用__import__()[...] 可能会发生某些副作用,例如导入父包,更新各种缓存(包括 sys.modules),[...]

因此,对于amain要查看相同的模块对象,我们需要手动检查和更新sys.modules. 因此,要从它们的路径加载模块,我们可以使用这个函数:

import sys, types

def load_from_path(name: str, path: str) -> types.ModuleType:
    if name not in sys.modules:
        spec = importlib.util.spec_from_file_location(name, path)
        mod = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(mod)
        sys.modules[name] = mod
    return sys.modules[name]

因此上面的例子变成了(假设load_from_path在所有模块中都定义了):

# b.py
eggs = 42
# a.py
import importlib.util
import os

B_PATH = os.path.join(os.path.dirname(__file__), 'b.py')
b = load_from_path('b', B_PATH)


spam = "ham"
print(f"{__name__} says {b.eggs=}")
# main.py
import importlib.util
import os

A_PATH = os.path.join(os.path.dirname(__file__), 'a.py')
B_PATH = os.path.join(os.path.dirname(__file__), 'b.py')

b = load_from_path('b', B_PATH)
b.eggs = "patched"
a = load_from_path('a', A_PATH)


print(f"{__name__} says {a.spam=}")
print(f"{__name__} says {b.eggs=}")

输出是:

a says b.eggs='patched'
__main__ says a.spam='ham'
__main__ says b.eggs='patched'
于 2020-06-10T10:13:49.327 回答