PYTHONPATH中可以多次提到具有共同包层次的模块吗?

6 投票
2 回答
991 浏览
提问于 2025-04-17 00:19

我有两个不同的项目,它们使用了相同的包名。只要这两个项目不同时出现在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 个回答

3

目前,Python 不支持来自不同目录的包。包是一个整体,不仅仅是一个命名空间。这和 Java 的“包”或 .NET 中更准确称为“命名空间”的东西是不同的。

当你导入一个包时,Python 会依次扫描 sys.path,并使用第一个匹配的结果。如果在路径中后面的某个目录里有一个同名的模块或包,它就找不到了。

顺便说一下,你的“备注”是不正确的。当你使用 import foo 时,Python 会先在 test.py 所在的目录里尝试相对导入,找不到匹配的东西,然后再尝试绝对导入模块 foo,结果也找不到,最后就会抛出一个 ImportError 错误。

与其用包名来给模块分组,使用一个共同的前缀,不如把包看作是小型的、自包含的库。在 Python 中,扁平结构比嵌套结构更好,拥有多个顶级包,每个包都有一个明确的功能,比起一个庞大的单一包要更好。与其用 org.example.fooorg.example.bar,不如直接用 foobar

4

虽然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

http://www.python.org/dev/peps/pep-0382/

撰写回答