from __future__ import absolute_import 实际上是做什么的? [英] What does from __future__ import absolute_import actually do?
问题描述
我有回答一个关于 Python 中绝对导入的问题,我认为我根据阅读Python 2.5 变更日志 和随附的 PEP.但是,在安装 Python 2.5 并尝试制作一个正确使用 from __future__ import absolute_import
的示例后,我意识到事情并不是那么清楚.
I have answered a question regarding absolute imports in Python, which I thought I understood based on reading the Python 2.5 changelog and 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.
直接从上面链接的更改日志中,这个语句准确地总结了我对绝对导入更改的理解:
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
这定义了一个名为 pkg
的包,其中包含 pkg.main
和 pkg.string
子模块.
This defines a package named pkg
containing the pkg.main
and pkg.string
submodules.
考虑 main.py 模块中的代码.如果它执行语句 import string
会发生什么?在 Python 2.4 及更早版本中,它将首先在包目录中查找以执行相对导入,找到 pkg/string.py,将该文件的内容作为 pkg.string
模块导入,然后该模块绑定到 pkg.main
模块命名空间中的名称 "string"
.
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.string
module, and that module is bound to the name "string"
in the pkg.main
module's namespace.
所以我创建了这个确切的目录结构:
So I created this exact directory structure:
$ ls -R
.:
pkg/
./pkg:
__init__.py main.py string.py
__init__.py
和 string.py
为空.main.py
包含以下代码:
__init__.py
and string.py
are empty. main.py
contains the following code:
import string
print string.ascii_uppercase
正如预期的那样,在 Python 2.5 中运行它会失败并返回 AttributeError
:
As expected, running this with Python 2.5 fails with an 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 更改日志中,我们发现了这一点(强调):
However, further along in the 2.5 changelog, we find this (emphasis added):
在 Python 2.5 中,您可以使用 from __future__ import absolute_import
指令将 import
的行为切换为绝对导入.这种绝对导入行为将成为未来版本(可能是 Python 2.7)的默认设置.一旦绝对导入成为默认值,import string
将始终找到标准库的版本.
In Python 2.5, you can switch
import
's behaviour to absolute imports using afrom __future__ import absolute_import
directive. This absolute-import behaviour will become the default in a future version (probably Python 2.7). Once absolute imports are the default,import string
will always find the standard library's version.
我因此创建了 pkg/main2.py
,与 main.py
相同,但具有额外的未来导入指令.现在看起来像这样:
I thus created pkg/main2.py
, identical to main.py
but with the additional future import directive. It now looks like this:
from __future__ import absolute_import
import string
print string.ascii_uppercase
使用 Python 2.5 运行此程序,但是...失败并显示 AttributeError
:
Running this with Python 2.5, however... fails with an 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-lib 版本的说法相矛盾.更重要的是,尽管警告说绝对导入将成为新的默认"行为,但我在使用 Python 2.7 时遇到了同样的问题,无论是否使用 __future__
指令:
This pretty flatly contradicts the statement that import string
will always find 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:
$ 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
语句在两个文件中都已更改):
as well as Python 3.5, with or without (assuming the print
statement is changed in both files):
$ 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
到 pkg
并直接从 REPL 运行导入.这些变化(或它们的组合)都没有改变上述结果.我无法将其与我所了解的关于 __future__
指令和绝对导入的内容相协调.
I have tested other variations of this. Instead of string.py
, I have created an empty module -- a directory named string
containing only an empty __init__.py
-- and instead of issuing imports from main.py
, I have cd
'd to pkg
and 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.
在我看来,这很容易通过以下内容来解释(这是来自 Python 2 文档,但此语句在 Python 3 的相同文档中保持不变):
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):
(...)
在程序启动时初始化,此列表的第一项 path[0]
是包含用于调用 Python 解释器的脚本的目录.如果脚本目录不可用(例如,如果交互式调用解释器或从标准输入读取脚本),path[0]
是空字符串,指示 Python 进行搜索首先是当前目录中的模块.
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.
那我错过了什么?为什么 __future__
语句看起来不像它所说的那样,这两个文档部分之间以及描述和实际行为之间的这种矛盾的解决方案是什么?
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?
推荐答案
变更日志措辞草率.from __future__ import absolute_import
不关心某些东西是否是标准库的一部分,并且 import string
不会总是为您提供绝对导入的标准库模块.
The changelog is sloppily worded. from __future__ import absolute_import
does not care about whether something is part of the standard library, and import string
will not always give you the standard-library module with absolute imports on.
from __future__ import absolute_import
意味着如果你import string
,Python 会一直寻找顶级的string
模块,而不是<代码>current_package.string.但是,它不会影响 Python 用来决定哪个文件是 string
模块的逻辑.当你这样做
from __future__ import absolute_import
means that if you import string
, Python will always look for a top-level string
module, rather than current_package.string
. However, it does not affect the logic Python uses to decide what file is the string
module. When you do
python pkg/script.py
pkg/script.py
看起来不像 Python 包的一部分.按照正常的流程,在路径中添加pkg
目录,pkg
目录下的所有.py
文件看起来都是顶级模块.import string
找到 pkg/string.py
不是因为它进行了相对导入,而是因为 pkg/string.py
似乎是最重要的 -级别模块 string
.这不是标准库 string
模块的事实没有出现.
pkg/script.py
doesn't look like part of a package to Python. Following the normal procedures, the pkg
directory is added to the path, and all .py
files in the pkg
directory look like top-level modules. import string
finds pkg/string.py
not because it's doing a relative import, but because pkg/string.py
appears to be the top-level module string
. The fact that this isn't the standard-library string
module doesn't come up.
要将文件作为 pkg
包的一部分运行,您可以这样做
To run the file as part of the pkg
package, you could do
python -m pkg.script
在这种情况下,pkg
目录将不会添加到路径中.但是,当前目录将被添加到路径中.
In this case, the pkg
directory will not be added to the path. However, the current directory will be added to the path.
您还可以向 pkg/script.py
添加一些样板文件,使 Python 将其视为 pkg
包的一部分,即使作为文件运行:
You can also add some boilerplate to pkg/script.py
to make Python treat it as part of the pkg
package even when run as a file:
if __name__ == '__main__' and __package__ is None:
__package__ = 'pkg'
但是,这不会影响 sys.path
.你需要一些额外的处理来从路径中删除 pkg
目录,如果 pkg
的父目录不在路径上,你需要坚持路径上也有.
However, this won't affect sys.path
. You'll need some additional handling to remove the pkg
directory from the path, and if pkg
's parent directory isn't on the path, you'll need to stick that on the path too.
这篇关于from __future__ import absolute_import 实际上是做什么的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!