PYTHONPATH中可以多次提到具有共同包层次的模块吗?
我有两个不同的项目,它们使用了相同的包名。只要这两个项目不同时出现在PYTHONPATH中,它们就能正常运行。但一旦它们都在PYTHONPATH里,其中一个项目就找不到自己项目里的导入内容了。
举个例子,这两个项目是这样的:
项目1:
x/
__init__.py
test.py
foo.py
test.py文件里有这么一行:
import x.foo
项目2:
x/
__init__.py
bar.py
如果我运行
PYTHONPATH=. python x/y/test.py
是没有错误的。但是如果我运行
PYTHONPATH='pathtoproject2:.' python x/test.py
就会出现错误:
Traceback (most recent call last):
File "x/test.py", line 1, in <module>
import x.foo
ImportError: No module named foo
有没有办法让不同的Python项目共享PYTHONPATH,同时又能使用相同的包名?还是说Python总是只会使用找到的第一个路径里的包?
注意:我知道如果把导入从x.foo改成import foo就能正常工作。但我想知道有没有可能在不修改任何包的情况下做到这一点。
2 个回答
目前,Python 不支持来自不同目录的包。包是一个整体,不仅仅是一个命名空间。这和 Java 的“包”或 .NET 中更准确称为“命名空间”的东西是不同的。
当你导入一个包时,Python 会依次扫描 sys.path
,并使用第一个匹配的结果。如果在路径中后面的某个目录里有一个同名的模块或包,它就找不到了。
顺便说一下,你的“备注”是不正确的。当你使用 import foo
时,Python 会先在 test.py
所在的目录里尝试相对导入,找不到匹配的东西,然后再尝试绝对导入模块 foo
,结果也找不到,最后就会抛出一个 ImportError
错误。
与其用包名来给模块分组,使用一个共同的前缀,不如把包看作是小型的、自包含的库。在 Python 中,扁平结构比嵌套结构更好,拥有多个顶级包,每个包都有一个明确的功能,比起一个庞大的单一包要更好。与其用 org.example.foo
和 org.example.bar
,不如直接用 foo
和 bar
。
虽然Python的导入机制本身不直接支持命名空间包,但我们可以通过一些方法来实现。你只需要在两个__init__.py文件中放入以下代码。
try:
import pkg_resources
pkg_resources.declare_namespace(__name__)
except ImportError:
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)
pkg_resources是由setuptools这个Python包提供的,它的好处是可以处理包含在egg压缩文件中的包。
pkgutil是Python标准库的一部分,所以如果你的系统中没有安装setuptools,我们就可以依赖它来处理命名空间的扩展。
关于Python命名空间包的更多信息可以在这里找到:
http://packages.python.org/distribute/setuptools.html#namespace-packages