简体   繁体   English

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

[英]Python: import module from another directory at the same level in project hierarchy

I've seen all sorts of examples and other similar questions, but I can't seem to find an example that exactly matches my scenario.我看过各种各样的例子和其他类似的问题,但我似乎找不到与我的场景完全匹配的例子。 I feel like a total goon asking this because there are so many similar questions, but I just can't seem to get this working "correctly."我觉得自己是个傻瓜,因为有很多类似的问题,但我似乎无法“正确”地解决这个问题。 Here is my project:这是我的项目:

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

If I move "CreateUser.py" to the main user_management directory, I can easily use: "import Modules.LDAPManager" to import LDAPManager.py --- this works.如果我将“CreateUser.py”移动到主 user_management 目录,我可以轻松地使用: "import Modules.LDAPManager"来导入 LDAPManager.py --- 这有效。 What I can't do (which I want to do), is keep CreateUser.py in the Scripts subfolder, and import LDAPManager.py.我不能做的(我想做的)是将 CreateUser.py 保留在 Scripts 子文件夹中,然后导入 LDAPManager.py。 I was hoping to accomplish this by using "import user_management.Modules.LDAPManager.py" .我希望通过使用"import user_management.Modules.LDAPManager.py"来实现这一点。 This doesn't work.这不起作用。 In short, I can get Python files to easily look deeper in the hierarchy, but I can't get a Python script to reference up one directory and down into another.简而言之,我可以让 Python 文件轻松地在层次结构中更深入地查看,但是我无法让 Python 脚本向上引用一个目录并向下引用另一个目录。

Note that I am able to solve my problem using:请注意,我可以使用以下方法解决我的问题:

sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
import Modules.LDAPManager as LDAPManager

I've heard that this is bad practice and discouraged.我听说这是不好的做法并且不鼓励。

The files in Scripts are intended to be executed directly (is the init .py in Scripts even necessary?). Scripts 中的文件旨在直接执行(甚至需要 Scripts 中的init .py 吗?)。 I've read that in this case, I should be executing CreateUser.py with the -m flag.我读过,在这种情况下,我应该使用 -m 标志执行 CreateUser.py。 I've tried some variations on this and just can't seem to get CreateUser.py to recognize LDAPManager.py.我已经尝试了一些变体,但似乎无法让 CreateUser.py 识别 LDAPManager.py。

If I move CreateUser.py to the main user_management directory, I can easily use: import Modules.LDAPManager to import LDAPManager.py --- this works.如果我将CreateUser.py移动到主 user_management 目录,我可以轻松地使用: import Modules.LDAPManager导入LDAPManager.py --- 这有效。

Please, don't .不要 In this way the LDAPManager module used by CreateUser will not be the same as the one imported via other imports.通过这种方式, LDAPManager通过使用模块CreateUser不会是一样的一个通过其他进口进口。 This can create problems when you have some global state in the module or during pickling/unpickling.当您在模块中或在酸洗/取消酸洗期间有一些全局状态时,这可能会产生问题。 Avoid imports that work only because the module happens to be in the same directory.避免仅因为模块恰好在同一目录中而起作用的导入。

When you have a package structure you should either:当你有一个包结构时,你应该:

  • Use relative imports, ie if the CreateUser.py is in Scripts/ :使用相对导入,即如果CreateUser.pyScripts/

     from ..Modules import LDAPManager

    Note that this was (note the past tense) discouraged by PEP 8 only because old versions of python didn't support them very well, but this problem was solved years ago.请注意, PEP 8不鼓励这样(注意过去时)只是因为旧版本的 python 不能很好地支持它们,但是这个问题在几年前就已经解决了。 The current version of PEP 8 does suggest them as an acceptable alternative to absolute imports.当前版本的 PEP 8确实建议将它们作为绝对导入的可接受替代方案。 I actually like them inside packages.我实际上喜欢它们在包装内。

  • Use absolute imports using the whole package name ( CreateUser.py in Scripts/ ):使用整个包名Scripts/ CreateUser.py )使用绝对导入:

     from user_management.Modules import LDAPManager

In order for the second one to work the package user_management should be installed inside the PYTHONPATH .为了让第二个工作,包user_management应该安装在PYTHONPATH During development you can configure the IDE so that this happens, without having to manually add calls to sys.path.append anywhere.在开发过程中,您可以配置 IDE 来实现这一点,而无需在任何地方手动添加对sys.path.append调用。

Also I find it odd that Scripts/ is a subpackage.我也觉得Scripts/是一个子包很奇怪。 Because in a real installation the user_management module would be installed under the site-packages found in the lib/ directory (whichever directory is used to install libraries in your OS), while the scripts should be installed under a bin/ directory (whichever contains executables for your OS).因为在实际安装中, user_management模块将安装在lib/目录中的site-packages下(无论哪个目录用于在您的操作系统中安装库),而脚本应安装在bin/目录下(无论哪个目录包含可执行文件)为您的操作系统)。

In fact I believe Script/ shouldn't even be under user_management .事实上,我认为Script/甚至不应该在user_management之下。 It should be at the same level of user_management .它应该与user_management处于同一级别。 In this way you do not have to use -m , but you simply have to make sure the package can be found (this again is a matter of configuring the IDE, installing the package correctly or using PYTHONPATH=. python Scripts/CreateUser.py to launch the scripts with the correct path).这样,您就不必使用-m ,但你只需要确保包可以发现(这又是配置IDE,正确安装包或使用的问题PYTHONPATH=. python Scripts/CreateUser.py以正确的路径启动脚本)。


In summary, the hierarchy I would use is:总之,将使用的层次结构是:

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

 Scripts/  (*not* a package)
        |  
        |----- CreateUser.py
        |----- FindUser.py

Then the code of CreateUser.py and FindUser.py should use absolute imports to import the modules:然后CreateUser.pyFindUser.py的代码应该使用绝对导入来导入模块:

from user_management.Modules import LDAPManager

During installation you make sure that user_management ends up somewhere in the PYTHONPATH , and the scripts inside the directory for executables so that they are able to find the modules.在安装期间,您确保user_management最终位于PYTHONPATH某个位置,以及可执行文件目录中的脚本,以便他们能够找到模块。 During development you either rely on IDE configuration, or you launch CreateUser.py adding the Scripts/ parent directory to the PYTHONPATH (I mean the directory that contains both user_management and Scripts ):在开发过程中,您要么依赖 IDE 配置,要么启动CreateUser.pyScripts/父目录添加到PYTHONPATH (我的意思是包含user_managementScripts的目录):

PYTHONPATH=/the/parent/directory python Scripts/CreateUser.py

Or you can modify the PYTHONPATH globally so that you don't have to specify this each time.或者您可以全局修改PYTHONPATH ,这样您就不必每次都指定它。 On unix OSes (linux, Mac OS X etc.) you can modify one of the shell scripts to define the PYTHONPATH external variable, on Windows you have to change the environmental variables settings.在 unix 操作系统(linux、Mac OS X 等)上,您可以修改其中一个 shell 脚本来定义PYTHONPATH外部变量,在 Windows 上,您必须更改环境变量设置。


Addendum I believe, if you are using python2, it's better to make sure to avoid implicit relative imports by putting:附录我相信,如果您使用的是 python2,最好通过放置以下内容来确保避免隐式相对导入:

from __future__ import absolute_import

at the top of your modules.在模块的顶部。 In this way import X always means to import the toplevel module X and will never try to import the X.py file that's in the same directory (if that directory isn't in the PYTHONPATH ).通过这种方式import X始终意味着导入顶层模块X并且永远不会尝试导入同一目录中的X.py文件(如果该目录不在PYTHONPATH )。 In this way the only way to do a relative import is to use the explicit syntax (the from . import X ), which is better ( explicit is better than implicit ).通过这种方式进行相对导入的唯一方法是使用显式语法( from . import X ),这是更好的(显式优于隐式)。

This will make sure you never happen to use the "bogus" implicit relative imports, since these would raise an ImportError clearly signalling that something is wrong.这将确保您永远不会碰巧使用“虚假”隐式相对导入,因为这些会引发ImportError清楚地表明出现问题。 Otherwise you could use a module that's not what you think it is.否则,您可能会使用与您认为不同的模块。

From Python 2.5 onwards, you can use从 Python 2.5 开始,您可以使用

from ..Modules import LDAPManager

The leading period takes you "up" a level in your heirarchy.领先时期将您在等级制度中“提升”一个级别。

See the Python docs on intra-package references for imports.请参阅有关导入的包内引用的 Python 文档。

In the "root" __init__.py you can also do a在“根” __init__.py您还可以执行

import sys
sys.path.insert(1, '.')

which should make both modules importable.这应该使两个模块都可以导入。

I faced the same issues.我遇到了同样的问题。 To solve this, I used export PYTHONPATH="$PWD" .为了解决这个问题,我使用了export PYTHONPATH="$PWD" However, in this case, you will need to modify imports in your Scripts dir depending on the below:但是,在这种情况下,您需要根据以下内容修改Scripts目录中的导入:

Case 1: If you are in the user_management dir, your scripts should use this style from Modules import LDAPManager to import module.案例 1:如果您在user_management目录中,您的scripts应该使用这种样式from Modules import LDAPManager来导入模块。

Case 2: If you are out of the user_management 1 level like main , your scripts should use this style from user_management.Modules import LDAPManager to import modules.情况 2:如果您像main一样不在user_management 1 级别,则您的scripts应该使用这种样式from user_management.Modules import LDAPManager来导入模块。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM