是否可以使用两个同名的Python包?
我有一个关于导入的疑问。这个问题可能听起来有点牵强,但目的是想探讨在一个包中使用绝对导入的局限性。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 个回答
其实,你应该使用命名空间(也叫包)来合理地分开你想要使用的模块。在你上面的代码中就是这样。
/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
来导入模块。
我最开始的测试(在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其实并不是特别适合处理多个同名的顶级包。
目前,我暂时认为在一般情况下这是不可能的,虽然在某些有限的情况下是可以做到的,但这种方法非常脆弱。
简单来说,Python不允许有两个同名的包。虽然有一种叫做“命名空间包”的东西,可以让一个包在多个目录中实现,但这需要相关的包能够相互配合。
PEP 8对显式相对导入的反对意见有点值得商榷,因为这会让重命名包以避免名字冲突变得更困难。如果这两个包使用相对导入,你可以简单地重命名其中一个,或者把它放在另一个包里,这样就解决了问题。
使用导入别名在这里也没用,因为最终在sys.modules
中显示的还是包的名字,而不是在导入模块中绑定的名字。
如果你想尝试一些更复杂的东西,可以自己写一个导入器(可以参考PEP 302和3.x importlib
文档)。如果你决定这么做,你几乎可以实现任何你想要的功能。