在不同目录中创建多个共享部分包结构的Python模块

2 投票
3 回答
2247 浏览
提问于 2025-04-15 13:25

我正在做一个Django项目,这个项目里只有一个应用。这个应用会根据GPL许可证发布,所以我想把它和我的个人网站分开开发。我的目标是根据我的域名来设置项目和应用的包结构,但在这方面我遇到了一些问题。

这是我的文件结构(适当的地方有__init__.py文件):

$HOME/django-sites/mydomain
$HOME/django-apps/mydomain/cms

还有我的PYTHONPATH:

$HOME/django-sites:$HOME/django-apps

如果我从文件系统的任何目录启动Python解释器,我可以从网站导入类,但不能从应用导入。如果我把PYTHONPATH中的两个条目的顺序反过来(先是应用,再是网站),我就可以从应用导入,但不能从网站导入。

看起来Python只会尝试从PYTHONPATH中第一个包含包名第一部分的条目导入。这是对的吗?这是正常现象吗?如果是这样的话,我只能把模块放在像domain/app1、domain/app2这样的包结构里,前提是它们在同一个目录结构下——不管PYTHONPATH怎么设置。

这并不是致命的问题,因为我可以重命名网站,但这和我预期的差别很大。Python教程提到过__path__,但我不知道怎么用:

包支持一个特殊属性,__path__。这个属性在执行包的__init__.py文件之前被初始化为一个列表,列表里包含了持有这个包的__init__.py文件的目录名。这个变量是可以修改的;修改后会影响将来查找包内模块和子包的方式。

虽然这个功能不常用,但可以用来扩展包中找到的模块集合。

有没有人遇到过类似的问题?我能用__path__做些什么来让它按预期工作吗?

3 个回答

1

你基本上有两个名字相同的模块(mydomain)。为什么不这样设置你的PYTHONPATH呢?

$HOME/django-sites:$HOME/django-apps/mydomain

这样可以避免你在导入时遇到的问题。

4

下面是关于一个包的 __init__.py 文件中 __path__ 的用法说明:

$ export PYTHONPATH=$HOME/django-sites
$ ls -d $HOME/django*
django-apps/  django-sites/ 
$ cat /tmp/django-sites/mydomain/__init__.py
import os

_components = __path__[0].split(os.path.sep)
if _components[-2] == 'django-sites':
  _components[-2] = 'django-apps'
  __path__.append(os.path.sep.join(_components))

$ python -c'import mydomain; import mydomain.foo'
foo here /tmp/django-apps/mydomain/foo.pyc
$ 

如你所见,这样做会把 django-apps/mydomain 里的内容变成 mydomain 包的一部分(而 mydomain__init__.py 文件在 django-sites/mydomain 下)——如果你用这种方法,就不需要把 django-apps 加到 sys.path 里。

至于这种安排是否比 @defrex 的回答建议的更好,这个可能见仁见智,但因为一个包扩展它的 __path__ 是 Python 程序员工具箱里相当重要的一部分,所以我觉得还是有必要给大家演示一下;-).

1

你的分析看起来没错。Python路径是用来找到要导入的模块的;Python会导入它找到的第一个模块。我不太确定你能做些什么来解决这个问题,除了给你的模块起不同的名字,或者把它们放在搜索路径的同一个位置。

撰写回答