我已经看过各种各样的例子和其他类似的问题,但我似乎无法找到一个与我的场景完全匹配的例子。我觉得这总是一个问题,因为有很多类似的问题,但我似乎无法让这个工作“正确”。这是我的项目:
user_management (package)
|
|------- __init__.py
|
|------- Modules/
| |
| |----- __init__.py
| |----- LDAPManager.py
| |----- PasswordManager.py
|
|------- Scripts/
| |
| |----- __init__.py
| |----- CreateUser.py
| |----- FindUser.py
如果我将“CreateUser.py”移动到主user_management目录,我可以轻松地使用:"import Modules.LDAPManager"
来导入LDAPManager.py ---这是有效的。我不能做的(我想做的)是将CreateUser.py保存在Scripts子文件夹中,然后导入LDAPManager.py。我希望通过使用"import user_management.Modules.LDAPManager.py"
来实现这一目标。这不起作用。简而言之,我可以让Python文件更容易在层次结构中更深入地查看,但是我无法使用Python脚本来引用一个目录并将其引用到另一个目录中。
请注意,我可以使用以下方法解决问题:
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
import Modules.LDAPManager as LDAPManager
我听说这是不好的做法而且气馁。
脚本中的文件是直接执行的(即使是必要的,脚本中的 init .py也是如此?)。我已经读过,在这种情况下,我应该使用-m标志执行CreateUser.py。我已经尝试了一些变体,似乎无法让CreateUser.py识别LDAPManager.py。
答案 0 :(得分:53)
如果我将
CreateUser.py
移动到主user_management目录,我可以 轻松使用:import Modules.LDAPManager
导入LDAPManager.py
---这很有效。
请不要。这样,LDAPManager
使用的CreateUser
模块不与通过其他导入导入的模块相同。当您在模块中有一些全局状态或在酸洗/去除涂层期间,这可能会产生问题。 避免导入仅仅因为模块恰好位于同一目录中而起作用。
当你有一个包结构时,你应该:
使用相对导入,即如果CreateUser.py
位于Scripts/
:
from ..Modules import LDAPManager
请注意,PEP 8不鼓励此 (请注意过去时态),因为旧版本的python不能很好地支持它们,但是这个几年前问题解决了。 PEP 8 的当前版本建议将它们作为绝对导入的可接受替代方案。我实际上喜欢在包内。
使用绝对导入使用整个软件包名称(CreateUser.py
中的Scripts/
):
from user_management.Modules import LDAPManager
为了使第二个工作,user_management
包应安装在PYTHONPATH
内。在开发期间,您可以配置IDE以便实现这一点,而无需在任何地方手动添加对sys.path.append
的调用。
此外,我发现Scripts/
是一个子包,这很奇怪。因为在实际安装中,user_management
模块将安装在site-packages
目录中的lib/
下(用于在操作系统中安装库的任何目录),同时应安装脚本在bin/
目录下(包含适用于您的操作系统的可执行文件)。
事实上,我认为Script/
甚至不应该在user_management
之下。它应该与user_management
处于同一水平。
通过这种方式,不必须使用-m
,但您只需确保可以找到包(这也是配置IDE,正确安装包或者使用PYTHONPATH=. python Scripts/CreateUser.py
以正确的路径启动脚本。)
总之,层次结构 I 将使用:
user_management (package)
|
|------- __init__.py
|
|------- Modules/
| |
| |----- __init__.py
| |----- LDAPManager.py
| |----- PasswordManager.py
|
Scripts/ (*not* a package)
|
|----- CreateUser.py
|----- FindUser.py
然后CreateUser.py
和FindUser.py
的代码应使用绝对导入来导入模块:
from user_management.Modules import LDAPManager
在安装过程中,确保user_management
最终位于PYTHONPATH
中的某个位置,以及目录中可执行文件的脚本,以便他们能够找到模块。在开发期间,您要么依赖IDE配置,要么启动CreateUser.py
将Scripts/
父目录添加到PYTHONPATH
(我的意思是包含user_management
和{{1}的目录}}):
Scripts
或者您可以全局修改PYTHONPATH=/the/parent/directory python Scripts/CreateUser.py
,这样您就不必每次都指定它。在unix操作系统(Linux,Mac OS X等)上,您可以修改其中一个shell脚本来定义PYTHONPATH
外部变量,在Windows上您必须更改环境变量设置。
附录我相信,如果您使用的是python2,最好通过放置以下内容来避免隐式相对导入:
PYTHONPATH
位于模块顶部。这样from __future__ import absolute_import
总是意味着导入 toplevel 模块import X
,并且永远不会尝试导入X
文件中的X.py
文件。同一目录(如果该目录不在PYTHONPATH
中)。这样, only 进行相对导入的方法是使用显式语法(from . import X
),这是更好的(显式更好比隐含的)。
这将确保您永远不会使用“虚假”隐式相对导入,因为这会使ImportError
明确表示出现问题。否则你可以使用一个不是你想象的模块。
答案 1 :(得分:13)
从Python 2.5开始,您可以使用
from ..Modules import LDAPManager
领先时期会让你“升级”到你的层次结构中。
请参阅intra-package references上有关导入的Python文档。
答案 2 :(得分:2)
在“root”__init__.py
中,您还可以执行
import sys
sys.path.insert(1, '.')
这应该使两个模块都可导入。
答案 3 :(得分:0)
我遇到了同样的问题。为了解决这个问题,我使用了export PYTHONPATH="$PWD"
。但是,在这种情况下,您需要根据以下内容修改Scripts
目录中的导入:
情况1:如果您位于user_management
目录中,则scripts
应该使用这种样式from Modules import LDAPManager
来导入模块。
情况2:如果您不属于user_management
1级,例如main
,则您的scripts
应该使用这种样式from user_management.Modules import LDAPManager
导入模块。