Python模块的绝对与显式相对导入

Dan*_*per 73 python package python-import

我想知道在Python应用程序中导入包的首选方法.我有这样的包结构:

project.app1.models
project.app1.views
project.app2.models
Run Code Online (Sandbox Code Playgroud)

project.app1.views进口project.app1.modelsproject.app2.models.有两种方法可以实现这一点.

绝对进口:

import A.A
import A.B.B
Run Code Online (Sandbox Code Playgroud)

或者使用PEP 328Python 2.5中引入的显式相对导入:

# explicit relative
from .. import A
from . import B
Run Code Online (Sandbox Code Playgroud)

什么是最pythonic的方式来做到这一点?

Ste*_*ano 116

不再强烈建议不要使用Python相对导入,但在这种情况下强烈建议使用absolute_import.

请看引用Guido本人的讨论:

"这不是历史性的吗?在实施新的相对导入语法之前,相对导入存在各种问题.短期解决方案是建议不使用它们.长期解决方案是实现明确的语法.现在现在是时候撤回反对推荐了.当然,不要过分 - 我仍然觉得他们有一种后天的味道;但他们有自己的位置."

OP正确链接PEP 328,其中说:

提出了几个用例,其中最重要的是能够重新排列大包的结构,而无需编辑子包.此外,如果没有相对导入,包内的模块不能轻易导入.

另请参阅几乎重复的问题何时或为何在Python中使用相对导入

当然,它仍然是品味的问题.虽然使用相对导入来移动代码更容易,但这也可能会意外地破坏事物; 并重命名进口并不困难.

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

from __future__ import absolute_import
Run Code Online (Sandbox Code Playgroud)

在这种情况下,隐式相对导入将不再可能(例如,import localfile将不再有效from . import localfile).为了清洁和面向未来的行为,建议使用absolute_import.

一个重要的警告是,由于 PEP 338PEP 366,相对导入需要将python文件作为模块导入 - 您无法执行具有相对导入的file.py或者您将获得ValueError: Attempted relative import in non-package.

在评估最佳方法时应考虑此限制.在任何情况下,Guido都反对从模块运行脚本:

我在这方面以及__main__机器的任何其他提议的twiddlings上都是-1.唯一的用例似乎是运行脚本,这些脚本恰好位于模块的目录中,我一直将其视为反模式.为了让我改变主意,你必须让我相信它不是.

关于此事的详尽讨论可以在SO上找到; 回覆.Python 3这是非常全面的:

  • Guido在2010年写道,它仍然在PEP中?如果PEP过时,我们如何信任? (9认同)
  • 从某种意义上说,PEP 就像美国的修正案,您可以修改内容。也有很多被拒绝的 PEPS。PEP 是提案。它们可以被接受、拒绝或过时,这通常意味着新的 PEP。PEP 8 是一个风格指南,因此可以就地修改。 (2认同)
  • 我很困惑"包内的模块不能轻易导入自己......"部分.我以前从未听说过导入模块的模块. (2认同)
  • 一个可能的例子@matiascelasco:如果你有foo/bar.py和foo/baz.py,还有baz.py在其他地方.如果你想从bar导入foo.baz,你可能想要确定你要导入的内容,例如.`import .baz` - 这只是PEP中描述的许多类似情况的一个简单化. (2认同)

Raf*_*ler 45

绝对进口.从PEP 8:

不鼓励进行包装内进口的相对进口.始终对所有导入使用绝对包路径.即使现在PEP 328 [7]已在Python 2.5中完全实现,但它的显式相对导入风格仍然是不鼓励的; 绝对导入更便携,通常更具可读性.

显式相对导入是一个很好的语言功能(我猜),但它们并不像绝对导入那样明确.更易读的形式是:

import A.A
import A.B.B
Run Code Online (Sandbox Code Playgroud)

特别是如果您导入几个不同的命名空间.如果你看一些包含从包中导入的编写良好的项目/教程,它们通常都遵循这种风格.

你想要更明确的几个额外的击键将在他们试图找出你的命名空间时(特别是如果你迁移到3.x,其中一些包)将来保存其他人(也许你)很多时间名字已经改变).

  • @Rafe:根据Guido的说法,PEP-8的那部分已经过时了.http://mail.python.org/pipermail/python-dev/2010-October/104476.html (61认同)
  • 现在,该声明已不再适用于PEP-8.它现在建议绝对进口,但相对进口是可接受的替代品. (10认同)
  • 我使用绝对导入的问题是在另一个包中使用包时.就我而言,它作为git子模块出现.在这种情况下,虽然我可以导入顶级包,但是无法导入任何低于此包的包,因为它们无法找到具有绝对导入的自己的模块.如果我在这个底层使用相对导入,那么一切正常. (4认同)

Bra*_*des 31

相对导入不仅让你可以在不改变数十个内部导入的情况下自由重命名你的包,但我也成功地解决了涉及循环导入或命名空间包等问题的某些问题,因为他们不会将Python发送回top"从顶级命名空间开始重新搜索下一个模块.

  • 请注意他的(Brandon Rhodes)对另一个答案的评论,其中一个链接表明不再气馁. (11认同)
  • 根据Python样式指南,这是气馁的风格.他们严重地忽略了可读性,并且不值得你提到的"方便".如果你需要使用相对导入来解决问题,那么你做错了. (4认同)
  • @meowsqueak我同意,有些软件包不容易安装(它们不是pip,你不想因为某种原因使用`python setup.py install`或`python setup.py develop`),在那些情况下我分叉源代码并将其添加为git子模块.当这些包在其自己的包名上使用绝对导入时,它们的导入将失败.唯一的解决方案是使用显式相对导入.这是我应该鼓励的. (3认同)
  • @RafeKettler你能解释一下如何在一个本身包含在另一个包中的包中使用绝对导入吗?绝对导入将在内部包中失败,因为它们不知道新的顶级。相对进口继续发挥作用。有人可能会争辩说,该包一开始就不应该嵌套在另一个包中,但有些代码应该是可重用的,而且这种情况经常发生。许多重用代码并未打包供公众使用,因此不会作为单独的包提供,因此最终会使用 VCS 导入/子模块等临时方法 (2认同)