可以在我的PYTHONPATH中多次提到具有公共包层次结构的模块吗?

时间:2011-08-25 07:44:40

标签: python package 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导入到导入foo,那么它将起作用。但是我想知道是否可以在不修改任何包的情况下进行。

2 个答案:

答案 0 :(得分:3)

目前,Python不支持来自不同目录的包。包是一个单元,而不仅仅是一个命名空间。这与Java“包”或.NET中更恰当命名的“命名空间”不同。

导入包时,Python将按顺序扫描sys.path,并使用第一个匹配项。如果路径中稍后出现的目录中有另一个具有匹配名称的模块或包,则无法找到它。

顺便说一下,你的“注意”是不正确的。当您使用import foo时,Python将尝试在test.py目录中进行相对导入,找不到匹配项,然后尝试绝对导入模块foo,该模块也不存在,然后提出ImportError

不要使用包名称来使用公共前缀对模块进行分组,而应将包视为小型的自包含库。在Python中,flat is better than nested,并且最好有多个顶级包,每个包都有一个不同的目的,而不是拥有一个大的单片包。而不是org.example.fooorg.example.bar,只需使用foobar

答案 1 :(得分:3)

虽然导入机制本身不支持,但有一种解决方法可以在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 zip文件中包含的包。

pkgutil 包含在python的标准库中,因此如果系统中未安装setuptools,我们依赖它来处理命名空间扩展。

有关python命名空间包的更多信息,请访问:

http://packages.python.org/distribute/setuptools.html#namespace-packages

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