当前位置: 首页 > 知识库问答 >
问题:

from__future__import absolute_import实际上是做什么的?

拓拔野
2023-03-14

我已经回答了一个关于Python中的绝对导入的问题,我认为通过阅读Python2.5更改日志和附带的PEP我理解了这个问题。但是,在安装Python2.5并尝试创建一个从__future__import absolute_import中正确使用的示例时,我意识到事情并不是那么清楚。

直接从上面链接的changelog,这个语句准确地概括了我对绝对导入更改的理解:

假设您有一个包目录如下所示:

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

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

请考虑main.py模块中的代码。如果它执行import string语句,会发生什么?在Python 2.4和更早版本中,它将首先查找包的目录以执行相对导入,找到pkg/string.py,将该文件的内容导入为pkg.string模块,并且该模块绑定到pkg.main模块名称空间中的名称“string”

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

$ ls -R
.:
pkg/

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

__init__.pystring.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中,您可以使用from__future__import absolute_import指令将import的行为切换为绝对导入。这种绝对导入行为将成为未来版本(可能是Python2.7)中的默认行为。一旦绝对导入成为默认值,import string将始终找到标准库的版本。

因此,我创建了pkg/main2.py,它与main.py相同,但带有额外的未来导入指令。现在看起来是这样的:

from __future__ import absolute_import
import string
print string.ascii_uppercase

但是,用Python2.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-lib版本的说法完全矛盾。此外,尽管警告绝对导入被安排为“新的默认”行为,但我还是使用了Python2.7,不管有没有__future__指令,都遇到了同样的问题:

$ 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发布导入,而是将cdd导入到pkg并直接从repl运行导入。这些变化(或它们的组合)都没有改变上述结果。我无法将这与我所读到的关于__future__指令和绝对导入的内容相一致。

在我看来,这很容易通过以下内容来解释(这是来自Python2文档,但在Python3的相同文档中该语句保持不变):

(...)

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

那我错过了什么?为什么__future__语句似乎没有按照它所说的做,这两部分文档之间的矛盾以及描述的行为和实际行为之间的矛盾是如何解决的呢?


共有1个答案

方德宇
2023-03-14

更改日志的措词草率。from__future__import absolute_import并不关心某些东西是否是标准库的一部分,import string并不总是为您提供带有绝对导入的标准库模块。

from__future__import absolute_import意味着如果您导入string,Python将始终查找顶级的string模块,而不是current_package.string。但是,它并不影响Python用来决定string模块是什么文件的逻辑。当你做的时候

python pkg/script.py

pkg/script.py看起来不像是Python包的一部分。按照正常过程,将pkg目录添加到路径中,pkg目录中的所有.py文件看起来都像是顶级模块。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的父目录不在路径上,您还需要将其粘贴到路径上。

 类似资料:
  • 我正试图把我的头缠在Apache Mesos上,需要澄清几个项目。 我对Mesos的理解是,它是一个安装在集群中的每个物理/VM服务器(“节点”)上的可执行文件,然后提供一个Java API(不知何故),将每个单独的节点视为计算资源(CPU/RAM/等)的集体池。因此,对于使用Java API编码的程序,他们只看到一组资源,而不必担心如何/在哪里部署代码。 因此,首先,我在这里的理解可能是根本错误

  • 我在看一个典型的for循环: 我对int I=1后的分号很满意:它是一个声明新变量的语句。如果i也是一个语句,为什么后面没有分号? 另一个例子。我打开Jshell并放置以下内容: 换句话说,命令可以工作,与是否有分号无关。我希望没有它就无法工作。 最后一个示例(改编自关于

  • 我只是第一次涉足iOS开发,我必须做的第一件事就是实现一个自定义容器视图控制器——让我们称之为SideBarViewController——它交换了几种可能的视图控制器中的哪一种它显示的子视图控制器,几乎与标准的Tab Bar Controller一模一样。(它几乎是一个标签栏控制器,但有一个可隐藏的侧菜单,而不是标签栏。) 根据Apple文档中的说明,每当我向容器中添加子ViewControll

  • 问题内容: 什么是真正做? 它的定义是 刷新实体时,此字段中保存的所有实体也会刷新 但这实际上意味着什么?有人可以给我一个简单的例子吗? 问题答案: 单独的CascadeType描述可能会有些混乱,但是有一种简单的方法可以从一般情况中找出来。 对于任何值,这意味着如果使用接口在实例上调用了操作,并且该实例具有对其他实体实例的引用,并且已经定义了关联,则该操作也将应用于该关联实体。 因此定义为: 从

  • 问题内容: 在构建RPM软件包的过程中,我必须指定BuildRoot,以后将在%install中使用它来侵害$ RPM_BUILD_ROOT。我一直认为$ RPM_BUILD_ROOT是RPM执行打包的假安装。然后,在使用RPM软件包进行安装时,它将安装到实际位置。例如: 我认为$ RPM_BUILD_ROOT仅用于打包过程,并且在某些方面,当用户执行“ rpm -ivh package.rpm”

  • 从文档中可以看出,-Xss用于设置JVM的堆栈大小。但我对这一说法感到非常困惑。 在Java中,每个线程都有自己的堆栈。-Xss指定的数字是否: > 线程的每个堆栈的大小。e、 如果将-Xss设置为256K,则每个线程将有一个256K大的堆栈。因此,10个线程将总共使用2560K。 非常感谢你。 编辑: 谢谢你的回答。看起来像是上面的(2)senario-Xss指定特定线程的最大堆栈大小。 然后我