Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 328 – 导入:多行和绝对/相对

作者:
Aahz <aahz at pythoncraft.com>
状态:
最终
类型:
标准跟踪
创建:
2003年12月21日
Python 版本:
2.4, 2.5, 2.6
历史记录:
2004年3月8日

目录

摘要

import 语句有两个问题

  • 长的 import 语句可能难以编写,需要各种技巧才能符合 Python 风格指南。
  • 在包存在的情况下,导入可能会产生歧义;在一个包中,不清楚 import foo 是指包内的模块还是包外的某个模块。(更准确地说,局部模块或包可能会隐藏直接挂在 sys.path 上的另一个模块。)

对于第一个问题,建议允许使用括号括住多个名称,从而允许 Python 的标准多行值机制适用。对于第二个问题,建议默认情况下所有 import 语句都为绝对导入(仅搜索 sys.path),并使用特殊语法(前导点)访问包相关的导入。

时间线

在 Python 2.5 中,您必须使用以下命令启用新的绝对导入行为:

from __future__ import absolute_import

您可以自由地使用相对导入。在 Python 2.6 中,任何导致包内导入的 import 语句都会引发 DeprecationWarning(这也适用于未使用相对导入语法的 from <> import)。

使用括号的理由

目前,如果您想从一个模块或包中导入许多名称,您必须选择以下几种不愉快的选项之一:

  • 使用反斜杠续行写一个长行
    from Tkinter import Tk, Frame, Button, Entry, Canvas, Text, \
        LEFT, DISABLED, NORMAL, RIDGE, END
    
  • 编写多个 import 语句
    from Tkinter import Tk, Frame, Button, Entry, Canvas, Text
    from Tkinter import LEFT, DISABLED, NORMAL, RIDGE, END
    

import * 不是一个选项 ;-)

相反,应该可以使用 Python 的标准分组机制(括号)来编写 import 语句:

from Tkinter import (Tk, Frame, Button, Entry, Canvas, Text,
    LEFT, DISABLED, NORMAL, RIDGE, END)

此提案的一部分从一开始就获得了 BDFL 的批准。

括号支持已添加到 Python 2.4 中。

绝对导入的理由

在 Python 2.4 及更早版本中,如果您正在读取位于包内的模块,则不清楚

import foo

是指顶级模块还是包内的另一个模块。随着 Python 库的扩展,越来越多的现有包内部模块意外地隐藏了标准库模块。在包内这是一个特别棘手的问题,因为没有办法指定哪个模块是目标。为了解决歧义,建议 foo 将始终是从 sys.path 可访问的模块或包。这称为绝对导入。

python-dev 社区选择绝对导入作为默认值,因为它们是更常见的用例,并且因为绝对导入可以提供相对(包内)导入的所有功能——尽管在重命名层次结构中较高处的包片段或将一个包移到另一个包内时会带来困难。

由于这代表了语义的变化,因此在 Python 2.5 和 2.6 中,绝对导入将是可选的,可以通过使用以下方式实现:

from __future__ import absolute_import

此提案的一部分从一开始就获得了 BDFL 的批准。

相对导入的理由

随着转向绝对导入,出现了是否应该允许相对导入的问题。提出了几个用例,其中最重要的是能够重新排列大型包的结构,而无需编辑子包。此外,包内的模块无法轻松地使用相对导入导入自身。

Guido 批准了相对导入的想法,但在拼写(语法)方面存在很多分歧。似乎大家一致认为,相对导入将需要列出要导入的特定名称(也就是说,import foo 作为裸词将始终是绝对导入)。

以下是竞争者:

  • Guido 提供的一个:
    from .foo import bar
    

    from ...foo import bar
    

    这两种形式有几个不同的建议语义。一个语义是使每个点代表一个级别。很多人抱怨难以计算点。另一个选项是只允许一个级别的相对导入。这会丢失很多功能,并且人们仍然抱怨在一个点形式中缺少点。最终的选项是定义一个查找相对模块和包的算法;这里的反对意见是“显式优于隐式”。(提出的算法是“从当前包目录向上搜索,直到找到最终的包父级”。)

    有些人建议使用其他标点符号作为分隔符,例如“-”或“^”。

    有些人建议使用“*”。

    from *.foo import bar
    
  • 下一组选项是从多个发帖者那里合并的:
    from __pkg__.__pkg__ import
    

    from .__parent__.__parent__ import
    

    许多人(包括 Guido)认为这些看起来很丑,但它们确实清晰且明确。总的来说,更多人更喜欢 __pkg__ 作为较短的选项。

  • 一个建议是只允许同级引用。换句话说,您将无法使用相对导入来引用包树中较高级别的模块。然后,您可以执行以下任一操作:
    from .spam import eggs
    

    import .spam.eggs
    
  • 有些人更喜欢允许索引父级:
    from -2.spam import eggs
    

    在这种情况下,从当前目录导入将很简单:

    from .spam import eggs
    
  • 最后,有些人不喜欢必须将 import 更改为 from ... import 以便深入挖掘包的方式。他们建议完全重写 import 语法:
    from MODULE import NAMES as RENAME searching HOW
    

    import NAMES as RENAME from MODULE searching HOW
        [from NAMES] [in WHERE] import ...
    

    但是,这很可能无法在 Python 2.5 中实现(更改太大),并且允许相对导入至关重要,因此我们现在需要一些东西(鉴于标准 import 将更改为绝对导入)。更重要的是,此建议的语法有几个悬而未决的问题:

    • 建议的精确语法是什么?(哪些子句在哪些情况下是可选的?)
    • searching 子句的约束力有多强?换句话说,您是否会写:
      import foo as bar searching XXX, spam as ham searching XXX
      

      import foo as bar, spam as ham searching XXX
      

Guido 的决定

Guido 已宣布[1]相对导入将使用前导点。单个前导点表示相对导入,从当前包开始。两个或多个前导点表示对当前包的父级进行相对导入,第一个点之后的每个点表示一个级别。以下是一个示例包布局:

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
        moduleY.py
    subpackage2/
        __init__.py
        moduleZ.py
    moduleA.py

假设当前文件是 moduleX.pysubpackage1/__init__.py,以下是新语法的正确用法:

from .moduleY import spam
from .moduleY import spam as ham
from . import moduleY
from ..subpackage1 import moduleY
from ..subpackage2.moduleZ import eggs
from ..moduleA import foo
from ...package import bar
from ...sys import path

请注意,虽然最后一种情况是合法的,但肯定不鼓励使用(Guido 使用的词是“疯狂”)。

相对导入必须始终使用 from <> importimport <> 始终是绝对的。当然,绝对导入可以通过省略前导点来使用 from <> import。禁止 import .foo 的原因是因为在

import XXX.YYY.ZZZ

然后

XXX.YYY.ZZZ

可在表达式中使用。但

.moduleY

不能在表达式中使用。

相对导入和 __name__

相对导入使用模块的 __name__ 属性来确定该模块在包层次结构中的位置。如果模块的名称不包含任何包信息(例如,它被设置为“__main__”),则相对导入将解析为模块是顶级模块,而不管模块在文件系统上的实际位置。

相对导入和 sys.modules 中的间接条目

引入包后,sys.modules 中间接条目的概念就出现了[2]。当 sys.modules 中包内模块的条目值为 None 时,表示该模块实际上引用了顶级模块。例如,'Sound.Effects.string' 在 sys.modules 中可能值为 None。这意味着解析为该名称的任何导入实际上都是要导入顶级 'string' 模块。

这引入了当相对导入旨在解析为绝对导入时的优化。但是,由于此 PEP 在绝对导入和相对导入之间做出了非常清晰的区分,因此不再需要此优化。当绝对/相对导入成为唯一可用的导入语义时,sys.modules 中的间接条目将不再受支持。

参考文献

有关更多背景信息,请参阅以下 python-dev 线程:


来源:https://github.com/python/peps/blob/main/peps/pep-0328.rst

上次修改:2023-09-09 17:39:29 GMT