Python模块的绝对导入和显式相对导入的区别

111

我想知道在Python应用程序中导入包的首选方式。我的包结构如下:

project.app1.models
project.app1.views
project.app2.models

project.app1.views导入了project.app1.modelsproject.app2.models。有两种方法可以做到这一点。

第一种是使用绝对导入:

import A.A
import A.B.B

或者使用直接相对导入,如PEP 328在Python 2.5中引入的:

# explicit relative
from .. import A
from . import B

最pythonic的方法是什么?

3个回答

148

Python相关导入不再被强烈反对,但在这种情况下强烈建议使用absolute_import。

请参见此讨论引用Guido本人的话:

“这不是历史吗?在新的相对导入语法实现之前,相对导入存在各种问题。短期解决方案是建议不要使用它们。长期解决方案是实现一个明确的语法。现在是时候撤回反建议了。当然,不要过分——我仍然认为它们是一种习得的口味;但是它们有它们的位置。”

OP正确地链接了PEP 328,其中提到:

提出了几种用例,其中最重要的是能够重新排列大型软件包的结构,而无需编辑子软件包。此外,一个包内的模块如果没有使用相对导入则无法轻松地导入自身。

同时请参考几乎相同的问题何时或为什么在Python中使用相对导入

当然,这仍然是一种口味问题。虽然使用相对导入可以更轻松地移动代码,但这也可能意外地破坏某些东西;而重命名导入并不那么困难。

要强制执行PEP 328的新行为,请使用:

from __future__ import absolute_import

在这种情况下,隐式相对导入将不再可行(例如,import localfile 不再起作用,只能使用 from . import localfile)。为了保持清洁和未来的证明,建议使用absolute_import。
一个重要的警告是,由于 PEP 338PEP 366 的缘故,相对导入需要把Python文件作为模块导入 - 不能执行有相对导入的文件.py否则会出现ValueError: Attempted relative import in non-package
在评估最佳方法时应考虑到这个限制。Guido反对从任何情况下的模块运行脚本:

我对此和对__main__机制的任何其他提议都是-1。 唯一的用例似乎是运行恰好位于模块目录中的脚本,这一直被看作是反模式。 要让我改变主意,你必须说服我它不是这样。

在stackoverflow上可以找到关于这个问题的详细讨论。对于Python 3,以下内容非常全面:


13
Guido写的是2010年的PEP,它仍然在使用吗?如果PEP已经过时了,我们怎么能相信它们呢? - Jabba
5
PEP(Python增强提案)就像美国宪法修正案一样,可以修改其中的内容。同时也有很多被否决的PEP。PEP是提案,可以被接受、拒绝或变得过时,这通常意味着需要新的PEP。PEP 8是一个风格指南,因此可以直接进行修改。 - CppLearner
3
我对“包中的模块不能轻松地导入自身......”这部分感到困惑。我之前从未听说过模块导入自己的情况。 - matiascelasco
3
一个可能的例子@matiascelasco:如果你有foo/bar.py和foo/baz.py,但是也有另外一个地方的baz.py。如果你想从bar导入foo.baz,你可能想要确定你正在导入什么,例如import .baz - 这只是PEP中描述的许多类似情况之一。 - Stefano
@ninMonkey同意Py3应该是答案的一部分。我以为问题是关于Py2的,但一些关于Py3的细节肯定会有所帮助。我个人也总是使用__future__ import absolute_import。我将在最后一段更新我的答案,并可能扩展我的理由,但PEP比我更详细和清晰;-) - Stefano
显示剩余2条评论

64

绝对导入。根据 PEP 8:

强烈不建议在包内部使用相对导入。 对于所有导入,请始终使用绝对包路径。 即使现在 Python 2.5 完全实现了 PEP 328 [7], 其显式相对导入的风格仍然被积极地不推荐; 绝对导入更具可移植性和通常更易读。

显式相对导入是一种不错的语言特性(我想是这样),但它们远不如绝对导入明确。更易读的形式为:

import A.A
import A.B.B

特别是当您导入多个不同的命名空间时。如果查看一些包含从包内导入的项目/教程,它们通常遵循这种风格。

您采用更加明确的方式所花费的额外按键次数将在未来节省他人(也可能是您自己)大量时间,当他们尝试弄清楚您的命名空间时(特别是如果您迁移到3.x,在其中一些包名称已更改的情况下)。


1
@Rafe,“看一些写得好的项目…” 有什么建议吗? - denis
@Denis,这并不是这样的。它大多数是用Python编写的。跟随Google代码链接并自行查看源代码。 - Rafe Kettler
74
据 Guido 称,PEP-8 中的那一部分已经过时了。@Rafe:http://mail.python.org/pipermail/python-dev/2010-October/104476.html - Brandon Rhodes
15
那个声明现在已经完全不在PEP-8中了。现在的说法是推荐使用绝对导入,但是相对导入也是可以接受的替代方案。 - dano
7
我对绝对导入的问题是在使用一个包中的另一个包时。在我的情况下,这个包是作为 git 子模块存在的。在这种情况下,尽管我可以导入顶层包,但任何底层包都无法使用绝对导入导入它们自己的模块。而如果我在底层使用相对导入,那么一切都能正常工作。 - davidA
显示剩余4条评论

47

相对导入不仅可以使您在以后重命名包时无需更改数十个内部导入,而且我还通过它们成功解决了涉及循环导入或命名空间包等问题的某些问题,因为它们不会将Python“回到顶部”以从顶级命名空间重新开始搜索下一个模块。


2
@RafeKettler 你能解释一下如何在一个被包含在另一个包中的包中使用绝对导入吗?由于内部包不知道新的顶层,绝对导入将失败。相对导入仍然有效。可能有人会认为包本来就不应该嵌套在另一个包中,但有些代码是可重用的,这种情况经常发生。许多重复使用的代码并没有打包为公共包,因此使用诸如VCS导入/子模块之类的临时方法。 - davidA
4
@meowsqueak 我同意,有些软件包不容易安装(它们不在pip上,你可能不想使用python setup.py installpython setup.py develop),在这种情况下,我会fork源代码并将其作为git子模块添加。当这些软件包在其自身的软件包名称上使用绝对导入时,它们的导入会失败。唯一的解决方法是使用显式相对导入。我认为应该鼓励这种做法。 - CMCDragonkai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接