是否可以使用两个同名的Python包?

19 投票
5 回答
20265 浏览
提问于 2025-04-16 12:12

我有一个关于导入的疑问。这个问题可能听起来有点牵强,但目的是想探讨在一个包中使用绝对导入的局限性。PEP8 强烈不建议使用相对导入(编辑:而且谷歌的Python风格指南也说绝对不要使用它们)。

假设你有两个比较大的包,它们的名字都一样,并且都按照PEP8使用绝对导入:

    /pkg1
        mod1.py (contains an absolute import: 'import pkg1.mod2')
        mod2.py
        ...

    /pkg1
        mod1.py (contains an absolute import: 'import pkg1.mod3')
        mod3.py
        ...

再假设你正在做一个Python项目,想要同时使用这两个包。这种情况可能会出现,比如你想在一个项目中使用同一个包的两个版本。

有没有办法把这两个包都放进你的项目结构中,这样你就可以在整个项目中自由使用这两个包里的模块呢?

对于解决方案,可以使用导入别名和临时修改sys.path等方法。但不可以更改任何一个包目录的内容。

5 个回答

0

其实,你应该使用命名空间(也叫包)来合理地分开你想要使用的模块。在你上面的代码中就是这样。

/pkg1
 mod1 - can just import mod2
 mod2.py
 __init__.py

/pkg2
 mod1 - can just import mod2
 mod2.py
 __init__.py

在其他地方,你应该根据需要使用 import pkg1.mod1 或者 import pkg2.mod1 来导入模块。

3

我最开始的测试(在Python 2.6和3.1中)表明,下面的做法可能有效:

import sys, re

import foo as foo1
for k in sys.modules:
    if re.match(r'foo(\.|$)', k):
        newk = k.replace('foo', 'foo1', 1)
        sys.modules[newk] = sys.modules[k]
        # The following may or may not be a good idea
        #sys.modules[newk].__name__ = newk
        del sys.modules[k]

sys.path.insert(0, './python')
import foo as foo2
for k in sys.modules:
    if re.match(r'foo(\.|$)', k):
        newk = k.replace('foo', 'foo2', 1)
        sys.modules[newk] = sys.modules[k]
        # The following may or may not be a good idea
        #sys.modules[newk].__name__ = newk
        del sys.modules[k]

不过,我只是在一些非常简单的包上进行了测试,而且只是出于好奇尝试的。一个问题是,这可能会影响到reload功能。Python其实并不是特别适合处理多个同名的顶级包。

目前,我暂时认为在一般情况下这是不可能的,虽然在某些有限的情况下是可以做到的,但这种方法非常脆弱。

22

简单来说,Python不允许有两个同名的包。虽然有一种叫做“命名空间包”的东西,可以让一个包在多个目录中实现,但这需要相关的包能够相互配合。

PEP 8对显式相对导入的反对意见有点值得商榷,因为这会让重命名包以避免名字冲突变得更困难。如果这两个包使用相对导入,你可以简单地重命名其中一个,或者把它放在另一个包里,这样就解决了问题。

使用导入别名在这里也没用,因为最终在sys.modules中显示的还是包的名字,而不是在导入模块中绑定的名字。

如果你想尝试一些更复杂的东西,可以自己写一个导入器(可以参考PEP 302和3.x importlib 文档)。如果你决定这么做,你几乎可以实现任何你想要的功能。

撰写回答