Python:从项目层次结构中同一级别的另一个目录导入模块

时间:2013-11-19 15:45:11

标签: python python-2.7 module path package

我已经看过各种各样的例子和其他类似的问题,但我似乎无法找到一个与我的场景完全匹配的例子。我觉得这总是一个问题,因为有很多类似的问题,但我似乎无法让这个工作“正确”。这是我的项目:

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。

4 个答案:

答案 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.pyFindUser.py的代码应使用绝对导入来导入模块:

from user_management.Modules import LDAPManager

在安装过程中,确保user_management最终位于PYTHONPATH中的某个位置,以及目录中可执行文件的脚本,以便他们能够找到模块。在开发期间,您要么依赖IDE配置,要么启动CreateUser.pyScripts/父目录添加到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导入模块。