Python from __future__ import absolute_import 实际上是做什么的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33743880/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 13:53:18  来源:igfitidea点击:

What does from __future__ import absolute_import actually do?

pythonpython-2.7python-importpython-2.5

提问by Two-Bit Alchemist

I have answereda question regarding absolute imports in Python, which I thought I understood based on reading the Python 2.5 changelogand accompanying PEP. However, upon installing Python 2.5 and attempting to craft an example of properly using from __future__ import absolute_import, I realize things are not so clear.

我已经回答了一个关于 Python 中绝对导入的问题,我认为我是根据阅读Python 2.5 更新日志和随附的PEP理解的。但是,在安装 Python 2.5 并尝试制作一个正确使用 的示例后from __future__ import absolute_import,我意识到事情并不是那么清楚。

Straight from the changelog linked above, this statement accurately summarized my understanding of the absolute import change:

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

Let's say you have a package directory like this:

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

This defines a package named pkgcontaining the pkg.mainand pkg.stringsubmodules.

Consider the code in the main.py module. What happens if it executes the statement import string? In Python 2.4 and earlier, it will first look in the package's directory to perform a relative import, finds pkg/string.py, imports the contents of that file as the pkg.stringmodule, and that module is bound to the name "string"in the pkg.mainmodule's namespace.

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

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模块的名称空间。

So I created this exact directory structure:

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

$ ls -R
.:
pkg/

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

__init__.pyand string.pyare empty. main.pycontains the following code:

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

import string
print string.ascii_uppercase

As expected, running this with Python 2.5 fails with an AttributeError:

正如预期的那样,使用 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'

However, further along in the 2.5 changelog, we find this (emphasis added):

然而,在 2.5 更新日志中,我们发现这一点(强调):

In Python 2.5, you can switch import's behaviour to absolute imports using a from __future__ import absolute_importdirective. This absolute-import behaviour will become the default in a future version (probably Python 2.7). Once absolute imports are the default, import stringwill always find the standard library's version.

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

I thus created pkg/main2.py, identical to main.pybut with the additional future import directive. It now looks like this:

因此pkg/main2.py,我创建了,与main.py附加的未来导入指令相同。现在看起来像这样:

from __future__ import absolute_import
import string
print string.ascii_uppercase

Running this with Python 2.5, however... fails with an AttributeError:

使用 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'

This pretty flatly contradicts the statement that import stringwill alwaysfind the std-lib version with absolute imports enabled. What's more, despite the warning that absolute imports are scheduled to become the "new default" behavior, I hit this same problem using both Python 2.7, with or without the __future__directive:

这个漂亮的断然反驳该语句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'

as well as Python 3.5, with or without (assuming the printstatement is changed in both files):

以及 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'


I have tested other variations of this. Instead of string.py, I have created an empty module -- a directory named stringcontaining only an empty __init__.py-- and instead of issuing imports from main.py, I have cd'd to pkgand run imports directly from the REPL. Neither of these variations (nor a combination of them) changed the results above. I cannot reconcile this with what I have read about the __future__directive and absolute imports.

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

It seems to me that this is easily explicable by the following(this is from the Python 2 docs but this statement remains unchanged in the same docs for Python 3):

在我看来,这很容易通过以下方式解释(这是来自 Python 2 文档,但该声明在 Python 3 的相同文档中保持不变):

sys.path

(...)

As initialized upon program startup, the first item of this list, path[0], is the directory containing the script that was used to invoke the Python interpreter. If the script directory is not available (e.g. if the interpreter is invoked interactively or if the script is read from standard input), path[0]is the empty string, which directs Python to search modules in the current directory first.

系统路径

(……)

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

So what am I missing? Why does the __future__statement seemingly not do what it says, and what is the resolution of this contradiction between these two sections of documentation, as well as between described and actual behavior?

那么我错过了什么?为什么__future__声明似乎没有按照它所说的做,这两个文档部分之间以及描述的行为和实际行为之间的这种矛盾的解决方案是什么?

采纳答案by user2357112 supports Monica

The changelog is sloppily worded. from __future__ import absolute_importdoes not care about whether something is part of the standard library, and import stringwill not always give you the standard-library module with absolute imports on.

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

from __future__ import absolute_importmeans that if you import string, Python will always look for a top-level stringmodule, rather than current_package.string. However, it does not affect the logic Python uses to decide what file is the stringmodule. When you do

from __future__ import absolute_import意味着如果你import string,Python 将始终寻找顶级string模块,而不是current_package.string. 但是,它不会影响 Python 用来决定哪个文件是string模块的逻辑。当你做

python pkg/script.py

pkg/script.pydoesn't look like part of a package to Python. Following the normal procedures, the pkgdirectory is added to the path, and all .pyfiles in the pkgdirectory look like top-level modules. import stringfinds pkg/string.pynot because it's doing a relative import, but because pkg/string.pyappears to be the top-level module string. The fact that this isn't the standard-library stringmodule doesn't come up.

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

To run the file as part of the pkgpackage, you could do

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

python -m pkg.script

In this case, the pkgdirectory will not be added to the path. However, the current directory will be added to the path.

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

You can also add some boilerplate to pkg/script.pyto make Python treat it as part of the pkgpackage even when run as a file:

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

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

However, this won't affect sys.path. You'll need some additional handling to remove the pkgdirectory from the path, and if pkg's parent directory isn't on the path, you'll need to stick that on the path too.

但是,这不会影响sys.path. 您需要一些额外的处理来pkg从路径中删除目录,如果pkg的父目录不在路径上,您也需要将其粘贴在路径上。

回答by Bakuriu

The difference between absolute and relative imports come into play only when you import a module from a package and that module imports an other submodule from that package. See the difference:

只有当您从包中导入模块并且该模块从该包中导入另一个子模块时,绝对导入和相对导入之间的区别才会发挥作用。看到不同:

$ 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
>>> 

In particular:

特别是:

$ 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

Note that python2 pkg/main2.pyhas a different behaviour then launching python2and then importing pkg.main2(which is equivalent to using the -mswitch).

请注意,它python2 pkg/main2.py具有不同的行为,然后启动python2然后导入pkg.main2(相当于使用-m开关)。

If you ever want to run a submodule of a package always use the -mswitch which prevents the interpreter for chaining the sys.pathlist and correctly handles the semantics of the submodule.

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

Also, I much prefer using explicit relative imports for package submodules since they provide more semantics and better error messages in case of failure.

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