Python 3中的相对导入无法正常工作
我有以下目录:
mydirectory ├── __init__.py ├── file1.py └── file2.py
我有一个函数f在file1.py中定义。
如果在file2.py中,我这样做
from .file1 import f
我得到以下错误:
SystemError:父模块未加载,无法执行相对导入
为什么? 以及如何使其工作?
由于file1
和file2
在同一个目录下,所以你甚至不需要有一个__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。