我正在为几个客户同时开发几个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 modules with the same name,但内部细节仍然不清楚。
无论如何,显而易见的解决方案是通过将项目目录转换为超级包来添加另一层命名空间限定:将__init__.py
文件添加到每个proj*
文件夹,并从{{{{1}行中删除这些文件夹。 1}}被扩展,例如
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
我不知道如何满足python解释器对文件夹结构的要求以及我同时拥有的要求。也许我可以使用一些符号链接创建一个额外的文件夹结构并在/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
,以便在项目和包名相同的情况下以一致的方式从所有项目导入?
答案 0 :(得分:16)
This是我问题的解决方案,虽然起初可能并不明显。
在我的项目中,我现在已经为每个客户引入了一个命名空间约定。在每个客户文件夹(cust1
,cust2
等)中,都有一个__init__.py
文件,其中包含以下代码:
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)
我的软件包中的所有其他__init__.py
文件都是空的(主要是因为我还没有时间知道如何处理它们)。
正如here所解释的那样,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
中首先出现的路径中的那个文件被执行,无论我从哪个项目导入内容。
答案 1 :(得分:0)
您应该使用优秀的virtualenv和virtualenvwrapper工具。
答案 2 :(得分:0)
如果您不小心从另一个客户/项目中导入代码并且没有注意到会发生什么?当你交付它几乎肯定会失败。我会采用一次为一个项目设置PYTHONPATH的约定,而不是试图让你所写的所有东西都可以一次导入。
您可以使用每个项目的包装器脚本来设置PYTHONPATH并启动python,或者在切换项目时使用脚本来切换环境。
当然,有些项目依赖于其他项目(您提到的那些库),但如果您打算让客户能够一次导入多个项目,那么您必须安排名称不要冲突。只有在PYTHONPATH上有多个项目不应该一起使用时,你才能遇到这个问题。