53

我有以下目录:

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

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

如果,在 file2.py 中,我会

from .file1 import f

我收到以下错误:

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

为什么?以及如何让它发挥作用?

4

4 回答 4

45

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

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

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

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

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

与任何其他想要使用库的代码一样file2.py导入的地方,绝对导入file1.pymydirectory

from mydirectory.file1 import f

当您setup.py为项目编写脚本时,您只需将mydirectory其列为一个包和file2.py一个脚本,一切都会正常工作。没必要折腾sys.path

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

python -m mydirectory.file1

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

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


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

这是错误的!

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

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

于 2015-10-18T06:42:39.150 回答
24

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

文档中说:

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

因此,正如@mrKelley所说,您需要在这种情况下使用绝对导入。

于 2013-05-19T17:45:27.557 回答
23

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

要在同一目录中的文件中导入某些内容,只需这样做

from file1 import f

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

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

于 2013-05-19T17:42:34.330 回答
0
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

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

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

在开头插入它的好处是在命名冲突的情况下保证在其他路径(甚至是内置路径)之前搜索路径。

于 2014-12-17T15:27:06.767 回答