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.py 或 subpackage1/__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 <> import;import <> 始终是绝对的。当然,绝对导入可以通过省略前导点来使用 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
上次修改时间:2025-02-01 08:59:27 GMT