在不同目录中创建多个共享部分包结构的Python模块
我正在做一个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 个回答
你基本上有两个名字相同的模块(mydomain)。为什么不这样设置你的PYTHONPATH呢?
$HOME/django-sites:$HOME/django-apps/mydomain
这样可以避免你在导入时遇到的问题。
下面是关于一个包的 __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 程序员工具箱里相当重要的一部分,所以我觉得还是有必要给大家演示一下;-).
你的分析看起来没错。Python路径是用来找到要导入的模块的;Python会导入它找到的第一个模块。我不太确定你能做些什么来解决这个问题,除了给你的模块起不同的名字,或者把它们放在搜索路径的同一个位置。