Python:在项目层次结构中从另一个目录导入模块。

时间:2021-04-03 23:19:32

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. What I can't do (which I want to do), is keep CreateUser.py in the Scripts subfolder, and import LDAPManager.py. I was hoping to accomplish this by using "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.

如果我移动”CreateUser。对于主user_management目录,我可以很容易地使用:“导入模块。进口LDAPManager LDAPManager”。py——这个工作。我不能做的(我想做的)是保留CreateUser。在Scripts子文件夹中,导入LDAPManager.py。我希望通过使用“import user_management.Modules.LDAPManager.py”来实现这一点。这并不工作。简而言之,我可以让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?). 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.

脚本中的文件打算直接执行(即init)。在脚本中,py甚至是必要的吗?)我已经读过了,在这种情况下,我应该执行CreateUser。带着-m标志的py。我已经尝试了一些变体,但似乎无法获得CreateUser。py认识到LDAPManager.py。

3 个解决方案

#1


48  

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目录,我可以很容易地使用:导入模块。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. 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.

请不要。这样,CreateUser使用的LDAPManager模块将与通过其他导入导入导入的模块不同。当您在模块中或在pickle / unpickle中有一些全局状态时,这会造成问题。避免导入只因为模块恰好在同一个目录中才有效。

When you have a package structure you should either:

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

  • Use relative imports, i.e if the CreateUser.py is in Scripts/:

    使用相对进口,我。如果CreateUser e。py脚本/:

     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.

    请注意,PEP 8之所以不鼓励这种用法(注意过去时态),仅仅是因为旧版本的python不太支持它们,但是这个问题在多年前就解决了。PEP 8的当前版本确实建议将它们作为绝对导入的可接受替代。实际上我喜欢放在包里。

  • Use absolute imports using the whole package name(CreateUser.py in 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. During development you can configure the IDE so that this happens, without having to manually add calls to sys.path.append anywhere.

为了让第二个工作,应该在PYTHONPATH中安装user_management包。在开发过程中,您可以配置IDE,这样就可以做到这一点,而无需手动添加对sys.path的调用。附加在任何地方。

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

而且我发现脚本/是子包也很奇怪。因为在真正的安装中,user_management模块将安装在lib/目录中的站点包(无论哪个目录用于在OS中安装库)下,而脚本应该安装在bin/目录下(无论哪个目录包含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).

事实上,我认为脚本/甚至不应该在user_management之下。它应该位于user_management的相同级别。通过这种方式,您不必使用-m,但您只需确保能够找到包(这同样是关于配置IDE、正确安装包或使用PYTHONPATH=的问题。python脚本/ 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的代码。py和FindUser。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. 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):

在安装过程中,您要确保user_management在PYTHONPATH中结束,而在可执行文件的目录中有脚本,以便它们能够找到模块。在开发过程中,您要么依赖IDE配置,要么启动CreateUser。py将脚本/父目录添加到PYTHONPATH(我的意思是包含user_management和脚本的目录):

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.

或者您可以全局修改PYTHONPATH,这样您就不必每次都指定它。在unix OS (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). 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).

在模块的顶部。这样,导入X总是意味着导入*模块X,并且永远不会尝试导入X。在同一目录中的py文件(如果该目录不在PYTHONPATH中)。这样,执行相对导入的惟一方法就是使用显式语法(from)。导入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. Otherwise you could use a module that's not what you think it is.

这将确保您永远不会碰巧使用“伪”隐含的相对进口,因为这将引发明显的恐慌,表明事情出了问题。否则,您可以使用一个模块,它不是您认为的那样。

#2


11  

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文档。

#3


1  

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

在“根”__init__。你也可以做a

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

which should make both modules importable.

这两个模块都应该是可导入的。

#1


48  

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目录,我可以很容易地使用:导入模块。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. 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.

请不要。这样,CreateUser使用的LDAPManager模块将与通过其他导入导入导入的模块不同。当您在模块中或在pickle / unpickle中有一些全局状态时,这会造成问题。避免导入只因为模块恰好在同一个目录中才有效。

When you have a package structure you should either:

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

  • Use relative imports, i.e if the CreateUser.py is in Scripts/:

    使用相对进口,我。如果CreateUser e。py脚本/:

     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.

    请注意,PEP 8之所以不鼓励这种用法(注意过去时态),仅仅是因为旧版本的python不太支持它们,但是这个问题在多年前就解决了。PEP 8的当前版本确实建议将它们作为绝对导入的可接受替代。实际上我喜欢放在包里。

  • Use absolute imports using the whole package name(CreateUser.py in 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. During development you can configure the IDE so that this happens, without having to manually add calls to sys.path.append anywhere.

为了让第二个工作,应该在PYTHONPATH中安装user_management包。在开发过程中,您可以配置IDE,这样就可以做到这一点,而无需手动添加对sys.path的调用。附加在任何地方。

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

而且我发现脚本/是子包也很奇怪。因为在真正的安装中,user_management模块将安装在lib/目录中的站点包(无论哪个目录用于在OS中安装库)下,而脚本应该安装在bin/目录下(无论哪个目录包含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).

事实上,我认为脚本/甚至不应该在user_management之下。它应该位于user_management的相同级别。通过这种方式,您不必使用-m,但您只需确保能够找到包(这同样是关于配置IDE、正确安装包或使用PYTHONPATH=的问题。python脚本/ 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的代码。py和FindUser。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. 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):

在安装过程中,您要确保user_management在PYTHONPATH中结束,而在可执行文件的目录中有脚本,以便它们能够找到模块。在开发过程中,您要么依赖IDE配置,要么启动CreateUser。py将脚本/父目录添加到PYTHONPATH(我的意思是包含user_management和脚本的目录):

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.

或者您可以全局修改PYTHONPATH,这样您就不必每次都指定它。在unix OS (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). 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).

在模块的顶部。这样,导入X总是意味着导入*模块X,并且永远不会尝试导入X。在同一目录中的py文件(如果该目录不在PYTHONPATH中)。这样,执行相对导入的惟一方法就是使用显式语法(from)。导入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. Otherwise you could use a module that's not what you think it is.

这将确保您永远不会碰巧使用“伪”隐含的相对进口,因为这将引发明显的恐慌,表明事情出了问题。否则,您可以使用一个模块,它不是您认为的那样。

#2


11  

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文档。

#3


1  

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

在“根”__init__。你也可以做a

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

which should make both modules importable.

这两个模块都应该是可导入的。