如何在Python3中使用带有if_uuname_uq=''uuu main_u'块的相对导入?

2024-04-26 13:18:38 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在制作一个包,这个包中的模块在if __name__=='__main__':块中有代码,用于测试。但是我尝试在这些模块中使用相对导入会导致错误。在

我读过这条线和其他十亿条线: Relative imports for the billionth time

在你把它标记为一个复制品之前,如果我想做的在Python3中是不可能的,那么我的问题是它为什么在Python2中工作,以及是什么促使了这个决定在Python3中引起如此大的麻烦?在


这是我的Python项目示例:

mypackage
- module1.py
- module2.py
- __init__.py

__init__.py和{}为空

module1.py包含:

^{pr2}$

这在Python身上效果很好。我可以从计算机上的任何地方导入module1,也可以直接运行module1并运行if块中的代码。在

然而,这种结构在Python3中不起作用。如果我尝试将模块导入其他位置,则会失败:

>>> from mypackage import module1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\_MyFiles\Programming\Python Modules\mypackage\module1.py", line 1, in <module>
    import module2
ModuleNotFoundError: No module named 'module2'

所以我试着把第一行改成from . import module2,然后修复了它,这样我就可以从任何地方成功地导入模块了。但是,当我尝试直接将module1作为脚本运行时,会出现以下错误:

Traceback (most recent call last):
  File "C:/_MyFiles/Programming/Python Modules/mypackage/module1.py", line 1, in <module>
    from . import module2
ImportError: cannot import name 'module2' from '__main__' (C:/_MyFiles/Programming/Python Projects/pgui/mypackage/module1.py)

我不想每次在处理模块时都打开控制台并键入python -m myfile,并希望直接将其作为脚本运行。在

我希望能够在不将父文件夹添加到PYTHONPATH的情况下使用Python2中的相对导入来处理模块

有没有更好的方法或解决这些问题的方法?在


Tags: 模块infrompyimportiflinepython3
3条回答

Python包不仅仅是您将代码插入的文件夹,导入行为不仅仅取决于您将代码插入了哪个文件夹。在

直接运行文件时,不会将其作为包的一部分运行。包级初始化不会运行,Python甚至无法识别包的存在。在python2上,隐式相对导入的存在意味着裸的import module2将解析为绝对导入或隐式相对导入,从而隐藏了问题,但导入结构仍然是不完整的。在python3上,隐式的相对导入已经消失(这是有原因的),因此问题很快就会显现出来。在

直接按文件名运行包的子模块并不能很好地工作。现在,我相信标准是要么使用-m,要么使用调用子模块功能的顶级入口点脚本。在

无论如何,有一种方法可以通过文件名运行,但这是一个样板。PEP 366的设计者似乎打算使用__package__ = 'appropriate.value'赋值来使相对导入正常工作,但这实际上还不够,即使您修复了导入路径。您还必须手动初始化父包,否则当您尝试运行相对导入时,您将收到“SystemError:parent module'foo'not loaded,cannot perform relative import”(系统错误:父模块'foo'未加载,无法执行相对导入)。完整的样板看起来更像

import os.path
import sys
if __name__ == '__main__' and __package__ is None:
    __package__ = 'mypackage'
    right_import_root = os.path.abspath(__file__)
    for i in range(__package__.count('.') + 2):
        right_import_root = os.path.dirname(right_import_root)

    # sys.path[0] is usually the right sys.path entry to replace, but this
    # may need further refinement in the presence of anything else that messes
    # with sys.path
    sys.path[0] = right_import_root
    __import__(__package__)

这在未来导入之类的内容之后,但在任何依赖于您的包的导入之前。在

我将把这个模板包装在一个可重用的函数中(使用堆栈操作来访问调用方的全局变量),只是如果您试图将该函数放在您的项目中的某个地方,那么在您修复了导入情况之前,您将无法导入该函数,而这正是您需要该函数来完成的。它可以作为可安装的依赖项工作。在

根据Module documentation,对于__main__模块,必须使用绝对导入。在

Note that relative imports are based on the name of the current module. Since the name of the main module is always "main", modules intended for use as the main module of a Python application must always use absolute imports.

所以只需将module1.py中的导入行改为:

from mypackage import module2

其他一切都没变。在

我最终也遇到了类似的情况,这让我困扰了很多,直到我意识到模块和包导入是如何工作的。在

考虑以下结构

mydir
- project
  - __init__.py
  - module1.py
  - module2.py

module1和{}的内容如下所示

module1.py

^{pr2}$

module2.py

从。导入模块1

print("Module 2")

if __name__ == '__main__':
    print("Executed as script")

现在,如果我在包目录之外打开一个repl并尝试导入它,那么它就可以工作了

Python 3.6.7 (default, Oct 22 2018, 11:32:17) 
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from package import module2
Module 1
Module 2
>>> sys.path
['', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/home/rbhanot/.local/lib/python3.6/site-packages', '/usr/local/lib/python3.6/dist-packages', '/usr/lib/python3/dist-packages']

请在sys.path处做一个注释,因为您可以看到它包含了作为第一项的当前目录,这意味着我的所有导入都将首先在当前目录中搜索。在

现在,如果我进入包目录,然后打开一个repl,并尝试进行相同的导入,看看会发生什么

Python 3.6.7 (default, Oct 22 2018, 11:32:17) 
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from . import module2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: cannot import name 'module2'
>>> import module2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/rbhanot/python-dotfiles/python3/modules-packages/mydir/package/module2.py", line 1, in <module>
    from . import module1
ImportError: attempted relative import with no known parent package
>>> import module1
Module 1
>>>

如您所见,导入失败,失败的原因是当我试图从包导入模块时,python会在sys.path中搜索任何名为package的包,因为我找不到任何包,因此导入失败。但是导入module1是可行的,因为它位于当前目录中。在

在包外,我可以将脚本执行为

python3 -m package.module2                                                                              2 ↵
Module 1
Module 2
Executed as script

虽然我可以执行脚本,但这不是它应该如何使用。记住,包是需要共享的代码库,不应该有任何可以通过命令行直接执行的代码。包中的包和模块只需导入,导入后,您可以编写脚本,通过在其中添加__name__子句来执行这些脚本。在

相关问题 更多 >