如何同时导入同一个Python模块的两个版本?

12

假设我有两个Python包的版本,比如"lib"。一个在文件夹~/version1/lib中,另一个在~/version2/lib中。我试图通过执行以下操作在一个会话中加载这两个包:

sys.path.insert(0, '~/version1')
import lib as a

sys.path.insert(0, '~/version2')
import lib as b

但它行不通,因为缓存的原因,b 将与 a 相同。

有没有什么方法可以做到呢?或许使用 sys.meta_path 中的钩子?我还没有想出来。

或者有没有办法删除已导入模块的缓存?


4
你是否解决了这个问题?你接受了一个答案,但声称它不起作用,这很奇怪。我现在也面临着同样的问题。 - TomCho
1
仍在寻找答案... - Metariat
3个回答

4

您需要从上一级导入它:

import version1.my_lib as a
import version2.my_lib as b

同时确保在所有的文件夹中都有一个__init__.py文件。

很遗憾,这是不可能的。因为在 my_lib 内部,会有一些其他文件 import my_lib.XXX.XXX。你的解决方案将导致内部导入失败 ImportError: No module named my_lib.XXX.XXX - demonguy
这是不可接受的。我不想修改代码,实际上这些代码不是我写的。 - demonguy

0
使用新的importlib接口,这是相对简单的:
import sys
from importlib.util import spec_from_file_location, module_from_spec
from pathlib import Path


def secondary_import(path: Path, name: str = None):
    if name is None:
        name = path.with_suffix('').name

    old_modules = {n: m for n, m in sys.modules.items() if n == name or n.startswith(name + ".")}
    for n in old_modules:
        del sys.modules[n]

    spec =  spec_from_file_location(name, str(path))
    module = module_from_spec(spec)
    sys.modules[name] = module
    spec.loader.exec_module(module)
    our_modules = {n: sys.modules[n] for n in old_modules}
    sys.modules.update(old_modules)
    return module, our_modules

然后可以像这样使用:


sys.path.insert(0, '~/version1')
import lib as a


sys.path.pop(0)

b, _ = secondary_import(os.path.join('~/version2', os.path.relpath(a.__path__, '~/version1')))

请注意,这可能很容易被破坏,特别是如果lib在导入方面做了任何奇怪的事情。 这包括:
  • 修改真正的全局状态(例如sys.path
  • 修改其他模块的全局状态
  • 函数内部的本地相对导入(有时用于修复循环依赖项)
  • 动态导入
  • 使用来自importlib.resourcesimportlib.metadata的内容
  • 非基本Python文件,如C扩展或命名空间包
其中一些可以解决,而另一些则无法解决。 我可能会创建一个软件包,以查看通常可以做哪些事情。

谢谢,这帮助我找到了另一种解决方案,即在sys.modules字典中给一个版本取另一个名称,并进行导入。 - undefined

-1
  • 您可以考虑使用Python虚拟环境
  • 激活所需的环境并在其中运行代码。

3
似乎不起作用,我想同时导入两个版本的模块。此外,我正在apache2服务器中运行这个python脚本,如何调用脚本并不由我控制。 - demonguy
我认为询问 sys.meta_path 是否是可能的解决方案的人知道什么是虚拟环境。 - Rol

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接