从__future__导入absolute_import实际起什么作用?


163

我已经回答了有关Python中绝对导入的问题,我认为通过阅读Python 2.5 changelog和随附的PEP可以理解。但是,在安装Python 2.5并尝试制作一个正确使用的示例时from __future__ import absolute_import,我意识到事情还不清楚。

直接从上面链接的更改日志,此语句准确总结了我对绝对导入更改的理解:

假设您有一个像这样的包目录:

pkg/
pkg/__init__.py
pkg/main.py
pkg/string.py

这定义了一个名为的包,pkg其中包含pkg.mainpkg.string子模块。

考虑main.py模块中的代码。如果执行该语句会import string怎样?在Python 2.4和更早的版本,它会先看看在包的目录进行相对进口,发现包装/ string.py,进口该文件的内容pkg.string模块,并且该模块被绑定到名字"string"pkg.main模块的名称空间。

所以我创建了这个确切的目录结构:

$ ls -R
.:
pkg/

./pkg:
__init__.py  main.py  string.py

__init__.py并且string.py是空的。main.py包含以下代码:

import string
print string.ascii_uppercase

不出所料,使用Python 2.5运行此命令失败,并显示AttributeError

$ python2.5 pkg/main.py
Traceback (most recent call last):
  File "pkg/main.py", line 2, in <module>
    print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'

但是,在2.5更新日志中,我们发现了这一点(添加了重点):

在Python 2.5中,您可以import使用from __future__ import absolute_import指令将的行为切换为绝对导入。在将来的版本(可能是Python 2.7)中,此绝对导入行为将成为默认设置。一旦将绝对导入设置为默认设置,import string就将始终找到标准库的版本。

因此pkg/main2.py,我创建了,main.py但与将来的import指令相同。现在看起来像这样:

from __future__ import absolute_import
import string
print string.ascii_uppercase

但是,使用Python 2.5运行它会失败AttributeError

$ python2.5 pkg/main2.py
Traceback (most recent call last):
  File "pkg/main2.py", line 3, in <module>
    print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'

这个漂亮的断然反驳该语句import string始终找到STD-库版本绝对进口启用。而且,尽管警告了绝对导入已计划成为“新的默认”行为,但无论使用还是不使用__future__指令,我都使用Python 2.7遇到了相同的问题:

$ python2.7 pkg/main.py
Traceback (most recent call last):
  File "pkg/main.py", line 2, in <module>
    print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'

$ python2.7 pkg/main2.py
Traceback (most recent call last):
  File "pkg/main2.py", line 3, in <module>
    print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'

以及Python 3.5(有无)(假设print两个文件中的语句均已更改):

$ python3.5 pkg/main.py
Traceback (most recent call last):
  File "pkg/main.py", line 2, in <module>
    print(string.ascii_uppercase)
AttributeError: module 'string' has no attribute 'ascii_uppercase'

$ python3.5 pkg/main2.py
Traceback (most recent call last):
  File "pkg/main2.py", line 3, in <module>
    print(string.ascii_uppercase)
AttributeError: module 'string' has no attribute 'ascii_uppercase'

我已经测试了其他变化。相反的string.py,我已经创建了一个空的模块-一个新的目录string仅包含一个空的__init__.py-而不是从发放的进口main.py,我有cd“d到pkg并直接从REPL运行的进口。这些变体(或它们的组合)都没有改变上面的结果。我无法将其与我已阅读的有关__future__指令和绝对导入的内容相协调。

在我看来,这很容易通过以下方式进行解释(这是来自Python 2文档,但该语句在适用于Python 3的同一文档中保持不变):

系统路径

(...)

在程序启动时进行初始化,该列表的第一项path[0]是包含用于调用Python解释器的脚本的目录。如果脚本目录不可用(例如,如果解释器是交互式调用的,或者从标准输入中读取了脚本),path[0]则为空字符串,字符串将Python首先引导到当前目录中的搜索模块。

那我想念什么呢?为什么该__future__声明似乎不按其要求行事?在文档的这两部分之间以及所描述的行为与实际行为之间,这种矛盾的解决方案是什么?


Answers:


104

变更日志的字眼很草率。from __future__ import absolute_import并不关心某些东西是否是标准库的一部分,import string也不会总是为您提供启用绝对导入的标准库模块。

from __future__ import absolute_import表示如果您愿意import string,Python将始终寻找顶级string模块,而不是current_package.string。但是,它不会影响Python用于确定string模块文件的逻辑。当你做

python pkg/script.py

pkg/script.py看起来不像是Python软件包的一部分。按照正常步骤,将pkg目录添加到路径,.py目录中的所有文件pkg看起来都像顶级模块。import string发现pkg/string.py不是因为它正在执行相对导入,而是因为它pkg/string.py似乎是顶级模块string。这不是标准库string模块的事实并没有出现。

要将文件作为pkg软件包的一部分运行,您可以

python -m pkg.script

在这种情况下,该pkg目录将不会添加到路径中。但是,当前目录将被添加到路径中。

您还可以添加一些样板,pkg/script.py以使Python pkg即使在作为文件运行时也将其视为包的一部分:

if __name__ == '__main__' and __package__ is None:
    __package__ = 'pkg'

但是,这不会影响sys.path。您需要进行一些其他处理才能pkg从路径中删除目录,并且如果pkg的父目录不在路径中,则也需要将其粘贴在路径中。


2
好,我的意思是,我明白了。这正是我的帖子所记录的行为。但是,面对这个问题,有两个问题:(1.)如果“那并非完全正确”,为什么文档会断然说是这样?并且,(2)如何,那么,你import string如果你不小心影吧,至少不通过膛线sys.modules。这不是from __future__ import absolute_import要预防的吗?它有什么作用?(PS,我不是拒绝投票的人。)
两位炼金术士

14
是的,那是我(对“没用”表示反对,不是对“错”表示反对)。从底部可以很清楚地了解到OP的sys.path工作原理,而实际的问题根本没有解决。也就是说,什么是from __future__ import absolute_import真正做到?
2015年

5
@ Two-BitAlchemist:1)变更日志措辞松散且不规范。2)您不再遮挡它。如果您使用自己的顶级模块将其遮盖起来,则即使浏览sys.modules也无法获得标准库string模块。from __future__ import absolute_import并不是要阻止顶级模块遮盖顶级模块;它应该阻止包内部模块掩盖顶级模块。如果您将文件作为pkg程序包的一部分运行,则程序包的内部文件将停止显示为顶级文件。
user2357112支持Monica15年

@ Two-BitAlchemist:答案已修改。这个版本更有用吗?
user2357112支持Monica15年

1
@storen:假设pkg是导入搜索路径上的一个包,应该是python -m pkg.main-m需要一个模块名称,而不是文件路径。
user2357112支持Monica

44

绝对导入和相对导入之间的差异仅在您从包中导入一个模块并且该模块从该包中导入另一个子模块时才起作用。看到不同:

$ mkdir pkg
$ touch pkg/__init__.py
$ touch pkg/string.py
$ echo 'import string;print(string.ascii_uppercase)' > pkg/main1.py
$ python2
Python 2.7.9 (default, Dec 13 2014, 18:02:08) [GCC] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pkg.main1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pkg/main1.py", line 1, in <module>
    import string;print(string.ascii_uppercase)
AttributeError: 'module' object has no attribute 'ascii_uppercase'
>>> 
$ echo 'from __future__ import absolute_import;import string;print(string.ascii_uppercase)' > pkg/main2.py
$ python2
Python 2.7.9 (default, Dec 13 2014, 18:02:08) [GCC] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pkg.main2
ABCDEFGHIJKLMNOPQRSTUVWXYZ
>>> 

特别是:

$ python2 pkg/main2.py
Traceback (most recent call last):
  File "pkg/main2.py", line 1, in <module>
    from __future__ import absolute_import;import string;print(string.ascii_uppercase)
AttributeError: 'module' object has no attribute 'ascii_uppercase'
$ python2
Python 2.7.9 (default, Dec 13 2014, 18:02:08) [GCC] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pkg.main2
ABCDEFGHIJKLMNOPQRSTUVWXYZ
>>> 
$ python2 -m pkg.main2
ABCDEFGHIJKLMNOPQRSTUVWXYZ

请注意,python2 pkg/main2.py启动python2和导入后的行为不同pkg.main2(等同于使用-m开关)。

如果要运行程序包的子模块,请始终使用该-m开关,该开关可防止解释器链接sys.path列表并正确处理子模块的语义。

另外,我更喜欢对包子模块使用显式相对导入,因为它们在失败的情况下提供了更多的语义和更好的错误消息。


因此,从本质上讲,它仅适用于避免了“当前目录”问题的狭窄情况?这似乎比PEP 328和2.5更新日志所描述的实现要弱得多。您认为文档不正确吗?
两位炼金术士

@ Two-BitAlchemist实际上,正在执行的是“窄例”。您仅启动了一个要执行的python文件,但这可能会触发数百次导入。包的子模块根本不应该执行,仅此而已。
Bakuriu

为什么 python2 pkg/main2.py会有不同的行为,然后启动python2然后导入pkg.main2?
储存

1
@storen那是因为相对导入的行为发生了变化。当您启动pkg/main2.pypython(版本2)时,不会将其pkg视为软件包。在使用python2 -m pkg.main2或导入它做的考虑到,pkg是一个包。
巴库里
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.