Python 3中的相对导入无法正常工作

我有以下目录:

mydirectory ├── __init__.py ├── file1.py └── file2.py 

我有一个函数f在file1.py中定义。

如果在file2.py中,我这样做

 from .file1 import f 

我得到以下错误:

SystemError:父模块未加载,无法执行相对导入

为什么? 以及如何使其工作?

由于file1file2在同一个目录下,所以你甚至不需要有一个__init__.py文件。 如果你要扩大规模,那就把它留在那里。

要在同一个目录中的文件中导入某些东西,就这样做

from file1 import f

即,您不需要执行相对path.file1因为它们位于相同的目录中。

如果你的主要函数,脚本或其他任何将要运行整个应用程序的东西都在另一个目录中,那么你将不得不把相对于正在执行的任何地方做成一切。

在一个包内启动模块作为可执行文件是一个不好的做法

当你开发一些东西的时候,你要么build立一个库,这个库是用来被其他程序导入的,所以直接执行它的子模块没有什么意义,或者你build立一个可执行文件,在这种情况下,没有理由使它成为一部分的一个包。

这就是为什么在setup.py区分包和脚本的原因。 软件包将在site-packages而脚本将安装在/usr/bin (或类似位置,取决于操作系统)下。

因此,我的build议是使用以下布局:

 / ├── mydirectory | ├── __init__.py | ├── file1.py └── file2.py 

其中file2.py导入file1.py作为任何其他代码,想要使用库mydirectory绝对导入

 from mydirectory.file1 import f 

当您为项目编写setup.py脚本时,只需将file2.py作为一个包列出,将file2.py作为一个脚本file2.py ,一切都将起作用。 不需要摆弄sys.path

如果您因为某种原因真的想要真正运行软件包的子模块,那么正确的方法是使用-m开关:

 python -m mydirectory.file1 

这将加载整个包,然后作为脚本执行该模块,从而允许相对导入成功。

我亲自避免这样做。 也因为很多人甚至不知道你能做到这一点,最终会得到和你一样的错误,并认为这个软件包是坏的。


关于目前接受的答案,它说,你应该使用一个from file1 import f隐式相对导入from file1 import f因为它将工作,因为它们在同一个目录中:

这是错误的

  • 不会在python3中隐式相对导入不允许,如果碰巧已经安装了file1模块(因为它将被导入,而不是你的模块!)肯定会中断。
  • 即使它工作, file1也不会被视为mydirectory包的一部分。 这可能很重要。

    例如,如果file1使用pickle ,则包的名称对于正确加载/卸载数据很重要。

启动python源文件时,禁止使用相对导入function导入当前包中的另一个文件。

在文件中说:

请注意,相对导入是基于当前模块的名称。 由于主模块的名称始终为“__main__”,因此用作Python应用程序主模块的模块必须始终使用绝对导入。

所以,正如@mrKelley所说的那样,你需要在这种情况下使用绝对导入。

 myproject/ mypackage ├── __init__.py ├── file1.py ├── file2.py └── file3.py mymainscript.py 

从一个文件导入到另一个文件的示例

 #file1.py from myproject import file2 from myproject.file3 import MyClass 

将包示例导入主文稿

 #mymainscript.py import mypackage 

https://docs.python.org/3/tutorial/modules.html#packages

https://docs.python.org/3/reference/import.html#regular-packages

https://docs.python.org/3/reference/simple_stmts.html#the-import-statement

https://docs.python.org/3/glossary.html#term-import-path

variablessys.path是确定解释器的模块searchpath的string列表。 它被初始化为从环境variablesPYTHONPATH获取的默认path,或者如果未设置PYTHONPATH,则从内置默认path初始化。 您可以使用标准列表操作对其进行修改:

 import sys sys.path.append('/ufs/guido/lib/python') sys.path.insert(0, '/ufs/guido/myhaxxlib/python') 

在开始时插入它有利于保证在命名冲突的情况下path在其他人(甚至是内置的)之前被search。