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

我已经看到了各种各样的例子和其他类似的问题,但我似乎找不到一个例子,完全符合我的情况。我觉得自己像个傻瓜一样问这个问题,因为有这么多类似的问题,但我似乎不能让这个工作“正确”这是我的项目:

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

我听说这是不好的做法和气馁。

The files in Scripts are intended to be executed directly (is the Init.py in Scripts even necessary?). I've read that in this case, I should be executing CreateUser.py with the -m flag. I've tried some variations on this and just can't seem to get CreateUser.py to recognize LDAPManager.py.

133649 次浏览

From Python 2.5 onwards, you can use

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.

If I move CreateUser.py to the main user_management directory, I can easily use: import Modules.LDAPManager to import LDAPManager.py --- this works.

Please, don't. In this way the LDAPManager module used by CreateUser will not be the same as the one imported via other imports. 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, i.e if the CreateUser.py is in Scripts/:

     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. The current version of PEP 8 does suggest them as an acceptable alternative to absolute imports. I actually like them inside packages.

  • Use absolute imports using the whole package name(CreateUser.py in Scripts/):

     from user_management.Modules import LDAPManager
    

In order for the second one to work the package user_management should be installed inside the PYTHONPATH. During development you can configure the IDE so that this happens, without having to manually add calls to sys.path.append anywhere.

Also I find it odd that Scripts/ is a subpackage. 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).

In fact I believe Script/ shouldn't even be under user_management. It should be at the same level of 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).


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:

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. 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):

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. 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.


Addendum I believe, if you are using python2, it's better to make sure to avoid implicit relative imports by putting:

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). 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).

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. Otherwise you could use a module that's not what you think it is.

In the "root" __init__.py you can also do a

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". However, in this case, you will need to modify imports in your Scripts dir depending on the below:

Case 1: If you are in the user_management dir, your scripts should use this style from Modules import LDAPManager to import module.

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.