python导入路径:在不同文件夹中具有相同名称的软件包
我正在同时为几个客户开发几个Python项目。我的项目文件夹结构的简化版本如下所示:
/path/
to/
projects/
cust1/
proj1/
pack1/
__init__.py
mod1.py
proj2/
pack2/
__init__.py
mod2.py
cust2/
proj3/
pack3/
__init__.py
mod3.py
例如,当我想使用中的功能时proj1
,我可以sys.path
通过扩展/path/to/projects/cust1/proj1
(例如,通过将文件设置PYTHONPATH
或添加到.pth
文件site_packages
夹或什至sys.path
直接修改)来扩展,然后像这样导入模块:
>>> from pack1.mod1 import something
当我从事更多项目时,碰巧不同的项目具有相同的包名称:
/path/
to/
projects/
cust3/
proj4/
pack1/ <-- same package name as in cust1/proj1 above
__init__.py
mod4.py
如果我现在简单地扩展sys.path
通过/path/to/projects/cust3/proj4
,我仍然可以从进口proj1
,而不是从proj4
:
>>> from pack1.mod1 import something
>>> from pack1.mod4 import something_else
ImportError: No module named mod4
我认为第二次导入失败的原因是Python仅在sys.path
找到pack1
包的位置搜索第一个文件夹,如果在其中找不到mod4
模块,则放弃搜索。我已经在较早的问题中对此进行了询问,请参阅具有相同名称的import
python模块,但是内部细节对我来说仍然不清楚。
无论如何,显而易见的解决方案是通过将项目目录转换为超级包来添加另一层名称空间限定:将__init__.py
文件添加到每个proj*
文件夹,并从sys.path
扩展的行中删除这些文件夹,例如
$ export PYTHONPATH=/path/to/projects/cust1:/path/to/projects/cust3
$ touch /path/to/projects/cust1/proj1/__init__.py
$ touch /path/to/projects/cust3/proj4/__init__.py
$ python
>>> from proj1.pack1.mod1 import something
>>> from proj4.pack1.mod4 import something_else
现在我遇到一种情况,即针对不同客户的不同项目具有相同的名称,例如
/path/
to/
projects/
cust3/
proj1/ <-- same project name as for cust1 above
__init__.py
pack4/
__init__.py
mod4.py
mod4
由于以前相同的原因,尝试从中导入不再起作用:
>>> from proj1.pack4.mod4 import yet_something_else
ImportError: No module named pack4.mod4
按照以前解决此问题的相同方法,我将添加另一个包/名称空间层,并将客户文件夹转换为超级超级包。
但是,这与我对项目文件夹结构的其他要求相冲突,例如
- 开发/发布结构以维护多个代码行
- 其他种类的源代码,例如JavaScript,SQL等。
- 源文件以外的其他文件,例如文档或数据。
某些项目文件夹的简化程度更低,更真实的描述如下所示:
/path/
to/
projects/
cust1/
proj1/
Development/
code/
javascript/
...
python/
pack1/
__init__.py
mod1.py
doc/
...
Release/
...
proj2/
Development/
code/
python/
pack2/
__init__.py
mod2.py
我看不到如何满足python解释器对文件夹结构和我同时具有的要求。也许我可以使用一些符号链接创建一个额外的文件夹结构,并在中使用它sys.path
,但是考虑到我已经做出的努力,我觉得我的整个方法从根本上来说是有问题的。在旁注中,我也很难相信python确实限制了我对源代码文件夹名称的选择,就像在所描述的情况下那样。
sys.path
如果存在名称相同的项目和程序包,如何设置项目文件夹,以便可以从所有项目中以一致的方式导入?
-
这是解决我的问题的方法,尽管起初可能并不明显。
在我的项目中,我现在介绍了一个约定,即每个客户一个名称空间。在每个客户文件夹(
cust1
,cust2
等)中,都有一个__init__.py
包含以下代码的文件:import pkgutil __path__ = pkgutil.extend_path(__path__, __name__)
__init__.py
程序包中的所有其他文件都是空的(主要是因为我还没有时间去弄清楚它们的其他用途)。正如解释在这里,
extend_path
可以确保Python是知道有一个包,身体的其他位置并在多个子包-
从我的理解-解释则不会停止搜索后未找到的第一个包路径下的模块它在中遇到sys.path
,但在中搜索所有路径__path__
。我现在可以以一致的方式访问所有项目之间的所有代码,例如
from cust1.proj1.pack1.mod1 import something from cust3.proj4.pack1.mod4 import something_else from cust3.proj1.pack4.mod4 import yet_something_else
不利的一面是,我不得不创建一个更深层次的项目文件夹结构:
/path/ to/ projects/ cust1/ proj1/ Development/ code/ python/ cust1/ __init__.py <--- contains code as described above proj1/ __init__.py <--- empty pack1/ __init__.py <--- empty mod1.py
但这对我来说似乎是可以接受的,尤其是考虑到我需要花很少的精力来维持这一惯例。为该项目
sys.path
扩展/path/to/projects/cust1/proj1/Development/code/python
。在一个旁注中,我注意到
__init__.py
在同一个客户的所有文件中sys.path
,无论我从哪个项目中导入内容,都会执行路径中第一个出现的文件。