PEP 279 – 内置函数 enumerate()
- 作者:
- Raymond Hettinger <python at rcn.com>
- 状态:
- 最终
- 类型:
- 标准跟踪
- 创建:
- 2002年1月30日
- Python 版本:
- 2.3
- 历史记录:
摘要
本 PEP 引入了一个新的内置函数 enumerate()
,用于简化一个常用的循环习惯用法。它为所有可迭代集合提供了与 iteritems()
为字典提供的相同优势——一种紧凑、易读、可靠的索引表示法。
基本原理
Python 2.2 引入了可迭代接口的概念,如 PEP 234 中所提议的那样。 iter()
工厂函数被提供为通用的调用约定,并且进行了深入的更改以使用迭代器作为贯穿 Python 的统一主题。统一的形式是为映射、序列和文件对象建立一个通用的可迭代接口。
生成器,如 PEP 255 中所提议的那样,被引入作为一种更易于创建迭代器的方法,特别是那些具有复杂内部执行或可变状态的迭代器。生成器的可用性使得改进 PEP 212 中的循环计数器想法成为可能。这些想法为带有索引和值的迭代提供了一个简洁的语法,但并不适用于所有可迭代对象。此外,这种方法没有生成器提供的内存友好优势,生成器不会一次性评估整个序列。
新的提案是添加一个内置函数 enumerate()
,一旦迭代器和生成器可用,该函数就成为可能。它为所有可迭代对象提供了与 iteritems()
为字典提供的相同优势——一种紧凑、易读、可靠的索引表示法。与 zip()
一样,预计它将成为一种常用的循环习惯用法。
此建议旨在利用现有的实现,并且需要很少的额外工作即可合并。它向后兼容,并且不需要任何新的关键字。该提案将在 Python 2.3 中生效,届时生成器将成为最终版本,并且不会从 __future__
中导入。
BDFL 声明
新的内置函数已被接受。
新内置函数规范
def enumerate(collection):
'Generates an indexed series: (0,coll[0]), (1,coll[1]) ...'
i = 0
it = iter(collection)
while 1:
yield (i, it.next())
i += 1
注释 A:PEP 212 循环计数器迭代讨论了实现索引的几种提案。一些提案仅适用于列表,与上述函数不同,该函数适用于任何生成器、xrange、序列或可迭代对象。此外,这些提案是在 Python 2.2 之前的世界中提出和评估的,Python 2.2 之前不包括生成器。因此,PEP 212 中的非生成器版本具有使用巨大的元组列表占用内存的缺点。此处提供的生成器版本快速且轻量,适用于所有可迭代对象,并允许用户在中途放弃序列而不会损失计算工作。
还有其他 PEP 涉及相关问题:整数迭代器、整数 for 循环,以及一个用于修改 range
和 xrange
的参数的 PEP。 enumerate()
提案并不排除其他提案,即使采用了其他提案,它仍然满足了一个重要的需求——需要计算任何可迭代对象中的项目。其他提案提供了一种生成索引的方法,但没有提供相应的 value。如果给定一个不支持随机访问的序列(例如文件对象、生成器或使用 __getitem__
定义的序列),这尤其成问题。
注释 B:几乎所有 PEP 评审人员都欢迎该函数,但对是否应该有任何内置函数存在分歧。支持单独模块的主要论点是为了减缓语言膨胀的速度。支持内置函数的主要论点是该函数注定将成为核心编程风格的一部分,适用于任何具有可迭代接口的对象。就像 zip()
解决循环遍历多个序列的问题一样, enumerate()
函数解决了循环计数器问题。
如果只允许一个内置函数,那么 enumerate()
是最重要的通用工具,它解决了最广泛的问题类别,同时提高了程序的简洁性、清晰度和可靠性。
注释 C:讨论了各种备选名称
iterindexed() |
五个音节太拗口了 |
index() |
不错的动词,但可能会与 .index() 方法混淆 |
indexed() |
广受欢迎,但应避免使用形容词 |
indexer() |
名词在 for 循环中读起来不太好 |
count() |
直接且明确,但在其他上下文中经常使用 |
itercount() |
直接、明确,并且被不止一个人讨厌 |
iteritems() |
与字典的键值概念冲突 |
itemize() |
令人困惑,因为 amap.items() != list(itemize(amap)) |
enum() |
简洁;不如 enumerate 清晰;与其他语言中的 enum 太相似,在其他语言中它具有不同的含义 |
所有涉及“count”的名称都进一步暗示计数将从 1 开始而不是从 0 开始。
所有涉及“index”的名称都与数据库语言中的用法冲突,在数据库语言中,索引暗示排序操作而不是线性排序。
注释 D:此函数最初是在可选的 start 和 stop 参数下提出的。GvR 指出,函数调用 enumerate(seqn,4,6)
具有另一种合理的解释,即切片,它将返回序列的第四个和第五个元素。为了避免歧义,即使这意味着失去作为循环计数器的灵活性,也放弃了可选参数。这种灵活性对于从 1 开始计数的常见情况(如下所示)最为重要。
for linenum, line in enumerate(source,1): print linenum, line
- GvR 的评论
filter
和map
应该消失并被列表推导式取代,而不是增加更多变体。我宁愿引入执行迭代器代数的内置函数(例如,我经常用作示例的 iterzip)。我喜欢能够并行迭代序列及其索引集的方法。将其作为内置函数是可以的。
我不喜欢“indexed”这个名字;形容词不适合用作函数名。也许是
iterindexed()
?- Ka-Ping Yee 的评论
- 我对您提出的所有内容都非常满意……额外的内置函数(特别是“indexed”)是我长期以来一直想要的东西。
- Neil Schemenauer 的评论
- 新的内置函数听起来不错。Guido 可能担心内置函数的数量过多。您最好将它们作为模块的一部分进行推广。如果您使用模块,则可以添加许多有用的函数(Haskell 有很多我们可以借鉴的函数)。
- Magnus Lie Hetland 的评论
- 我认为 indexed 将是一个有用且自然的内置函数。我肯定会经常使用它。我非常喜欢
indexed()
;+1。我很乐意让它使 PEP 281 过时。为迭代器实用程序添加一个单独的模块似乎是个好主意。 - 社区的评论
- 对
enumerate()
提案的回应几乎 100% 都是积极的。几乎每个人都喜欢这个想法。 - 作者回复
- 在这些评论之前,提出了四个内置函数。在评论之后,
xmap
、xfilter
和xzip
被撤回。剩下的一个对语言至关重要,并且单独提出。Indexed()
非常容易实现,可以在几分钟内完成文档编写。更重要的是,它在日常编程中很有用,而日常编程并不涉及生成器的显式使用。此提案最初还包括另一个函数
iterzip()
。随后,它在itertools
模块中作为izip()
函数实现。
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0279.rst
上次修改时间:2023-09-09 17:39:29 GMT