Python 3中的相对导入不起作用

Relative import in Python 3 not working

我有以下目录:

1
2
3
4
mydirectory
├── __init__.py
├── file1.py
└── file2.py

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

如果,在file2.py中,我有

1
from .file1 import f

我得到以下错误:

SystemError: Parent module '' not loaded, cannot perform relative
import

为什么?如何使其发挥作用?


在包中作为可执行文件启动模块是一种糟糕的做法。

当您开发某个东西时,您要么构建一个库,该库是由其他程序导入的,因此直接执行其子模块没有多大意义,要么构建一个可执行文件,在这种情况下,没有理由使它成为包的一部分。

这就是为什么在setup.py中,您要区分包和脚本。这些包将在site-packages下运行,而脚本将安装在/usr/bin下(或类似位置,具体取决于操作系统)。

因此,我建议使用以下布局:

1
2
3
4
5
/
├── mydirectory
|    ├── __init__.py
|    ├── file1.py
└── file2.py

如果file2.py作为任何其他想要使用库mydirectory的代码导入file1.py,则绝对导入:

1
from mydirectory.file1 import f

当你为项目写一个setup.py脚本时,你只需把mydirectory列为一个包,把file2.py列为脚本,一切都会正常工作。不需要摆弄sys.path

如果您出于某种原因确实想运行包的子模块,那么正确的方法是使用-m开关:

1
python -m mydirectory.file1

这将加载整个包,然后以脚本的形式执行模块,从而允许相对导入成功。

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

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

这是错误的!

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

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


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

要在同一目录的文件中导入某些内容,请执行以下操作

埃多克斯1〔3〕

也就是说,您不需要执行相对路径.file1,因为它们在同一目录中。

如果运行整个应用程序的主函数、脚本或其他内容在另一个目录中,那么您必须使所有内容都与正在执行的位置相关。


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

在文件中说:

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

所以,正如@mrkelley所说,在这种情况下,你需要使用绝对进口。


1
2
3
4
5
6
7
8
9
myproject/

mypackage
├── __init__.py
├── file1.py
├── file2.py
└── file3.py

mymainscript.py

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

1
2
3
#file1.py
from myproject import file2
from myproject.file3 import MyClass

将包示例导入到主脚本

1
2
#mymainscript.py
import mypackage

https://docs.python.org/3/tutorial/modules.html包

https://docs.python.org/3/reference/import.html常规包

https://docs.python.org/3/reference/simple_stmts.html导入语句

https://docs.python.org/3/glossary.html term导入路径

变量sys.path是确定解释器对模块的搜索路径的字符串列表。它初始化为从环境变量pythonpath获取的默认路径,或者如果未设置pythonpath,则从内置默认路径获取。您可以使用标准列表操作修改它:

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

在开头插入它有利于确保在命名冲突的情况下,在其他路径(甚至内置路径)之前搜索路径。