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

Cpt*_*rkt 74 python module path package python-2.7

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

user_management  (package)
        |
        |------- __init__.py
        |
        |------- Modules/
        |           |
        |           |----- __init__.py
        |           |----- LDAPManager.py
        |           |----- PasswordManager.py
        |
        |------- Scripts/
        |           |
        |           |----- __init__.py
        |           |----- CreateUser.py
        |           |----- FindUser.py
Run Code Online (Sandbox Code Playgroud)

如果我将"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
Run Code Online (Sandbox Code Playgroud)

我听说这是不好的做法而且气馁.

脚本中的文件是直接执行的(脚本中的init .py甚至是必要的吗?).我已经读过,在这种情况下,我应该使用-m标志执行CreateUser.py.我已经尝试了一些变体,似乎无法让CreateUser.py识别LDAPManager.py.

Bak*_*riu 61

如果我移动CreateUser.py到主user_management目录,我可以轻松使用:import Modules.LDAPManager导入LDAPManager.py ---这是有效的.

拜托,不要.通过这种方式,LDAPManager所用模块CreateUser不会是一样的一个通过其他进口进口.当您在模块中有一些全局状态或在酸洗/去除涂层期间,这可能会产生问题.避免仅仅因为模块恰好位于同一目录中而导致的导入.

当你有一个包结构时,你应该:

  • 使用相对导入,即如果CreateUser.pyScripts/:

     from ..Modules import LDAPManager
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,PEP 8不鼓励这一点(注意过去时),因为旧版本的python不能很好地支持它们,但这个问题在几年前就解决了.PEP 8 的当前版本确实表明它们是绝对进口的可接受替代品.我实际上喜欢它们在包内.

  • 使用整个包名称(CreateUser.pyin Scripts/)使用绝对导入:

     from user_management.Modules import LDAPManager
    
    Run Code Online (Sandbox Code Playgroud)

为了使第二个工作,包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与正确的路径启动脚本).


总之,将使用的层次结构是:

user_management  (package)
        |
        |------- __init__.py
        |
        |------- Modules/
        |           |
        |           |----- __init__.py
        |           |----- LDAPManager.py
        |           |----- PasswordManager.py
        |

 Scripts/  (*not* a package)
        |  
        |----- CreateUser.py
        |----- FindUser.py
Run Code Online (Sandbox Code Playgroud)

然后代码CreateUser.pyFindUser.py应该使用绝对导入来导入模块:

from user_management.Modules import LDAPManager
Run Code Online (Sandbox Code Playgroud)

在安装过程中,确保user_management最终PYTHONPATH位于目录中的某个位置,以及可执行文件的目录中的脚本,以便它们能够找到模块.在开发过程中你要么依靠IDE配置,或在启动CreateUser.py添加Scripts/父目录的PYTHONPATH(我是指既包含目录user_managementScripts):

PYTHONPATH=/the/parent/directory python Scripts/CreateUser.py
Run Code Online (Sandbox Code Playgroud)

或者您可以PYTHONPATH全局修改,这样您就不必每次都指定它.在unix操作系统(Linux,Mac OS X等)上,您可以修改其中一个shell脚本来定义PYTHONPATH外部变量,在Windows上您必须更改环境变量设置.


附录我相信,如果您使用的是python2,最好通过放置以下内容来确保避免隐式相对导入:

from __future__ import absolute_import
Run Code Online (Sandbox Code Playgroud)

在模块的顶部.这样import X 总是意味着导入顶层模块X,永远不会尝试导入X.py同一目录中的文件(如果该目录不在其中PYTHONPATH).通过这种方式,进行相对导入的唯一方法是使用显式语法(the from . import X),这更好(显式优于隐式).

这将确保你永远不会碰巧使用"虚假"的隐式相对进口,因为这会引发一个ImportError明确的信号,即出现问题.否则你可以使用一个不是你认为的模块.


jon*_*rpe 14

从Python 2.5开始,您可以使用

from ..Modules import LDAPManager
Run Code Online (Sandbox Code Playgroud)

领先时期会让你"升级"到你的层次结构中.

请参阅有关导入的包内引用的Python文档.


rdo*_*dev 6

在“根”中,__init__.py您还可以执行

import sys
sys.path.insert(1, '.')
Run Code Online (Sandbox Code Playgroud)

这应该使两个模块都可以导入。