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

Python 增强提案

PEP 201 – 步调一致的迭代

作者:
Barry Warsaw <barry at python.org>
状态:
最终
类型:
标准跟踪
创建:
2000年7月13日
Python 版本:
2.0
历史记录:
2000年7月27日

目录

引言

本 PEP 描述了“步调一致的迭代”提案。本 PEP 跟踪此功能的状态和所有权,计划在 Python 2.0 中引入。它包含对该功能的描述,并概述了支持该功能所需的更改。本 PEP 总结了在邮件列表论坛中进行的讨论,并在适当的地方提供了更多信息的 URL。此文件的 CVS 修订历史包含权威的历史记录。

动机

Python 中标准的 for 循环会迭代序列中的每个元素,直到序列耗尽[1]。但是,for 循环只迭代单个序列,并且通常希望以步调一致的方式迭代多个序列。换句话说,以这样的方式:循环的第 i 次迭代返回一个包含每个序列的第 i 个元素的对象。

实现此目的的常用习惯用法不直观。本 PEP 提出了一种通过引入一个名为 zip 的新内置函数来执行此类迭代的标准方法。

虽然 zip() 的主要动机来自步调一致的迭代,但通过将 zip() 实现为内置函数,它在 for 循环之外的其他上下文中也具有额外的实用性。

步调一致的 for 循环

步调一致的 for 循环是对两个或多个序列的非嵌套迭代,使得在每次循环通过时,从每个序列中获取一个元素来组成目标。这种行为可以通过使用 map() 内置函数在 Python 中实现。

>>> a = (1, 2, 3)
>>> b = (4, 5, 6)
>>> for i in map(None, a, b): print i
...
(1, 4)
(2, 5)
(3, 6)
>>> map(None, a, b)
[(1, 4), (2, 5), (3, 6)]

for 循环只是像往常一样迭代此列表。

虽然 map() 习惯用法在 Python 中很常见,但它有几个缺点。

  • 对于没有函数式编程背景的程序员来说,它并不明显。
  • 使用魔术 None 作为第一个参数并不明显。
  • 当列表长度不相等时,它具有任意、通常是意外的和不灵活的语义:较短的序列用 None 填充。
    >>> c = (4, 5, 6, 7)
    >>> map(None, a, c)
    [(1, 4), (2, 5), (3, 6), (None, 7)]
    

由于这些原因,在 Python 2.0 测试版阶段,提出了几个关于步调一致的 for 循环的语法支持的提案。以下是有两个建议。

for x in seq1, y in seq2:
  # stuff
for x, y in seq1, seq2:
  # stuff

这些形式都不起作用,因为它们在 Python 中都已具有含义,并且更改含义会破坏现有代码。所有其他关于新语法的建议都存在相同的问题,或者与另一个名为“列表推导式”的提议功能冲突(参见PEP 202)。

提出的解决方案

提出的解决方案是引入一个新的内置序列生成器函数,可在 __builtin__ 模块中使用。此函数称为 zip,具有以下签名。

zip(seqa, [seqb, [...]])

zip() 接受一个或多个序列,并将它们的元素编织在一起,就像 map(None, ...) 对长度相等的序列所做的那样。当最短的序列耗尽时,编织停止。

返回值

zip() 返回一个真正的 Python 列表,就像 map() 一样。

示例

以下是一些基于下面参考实现的示例。

>>> a = (1, 2, 3, 4)
>>> b = (5, 6, 7, 8)
>>> c = (9, 10, 11)
>>> d = (12, 13)

>>> zip(a, b)
[(1, 5), (2, 6), (3, 7), (4, 8)]

>>> zip(a, d)
[(1, 12), (2, 13)]

>>> zip(a, b, c, d)
[(1, 5, 9, 12), (2, 6, 10, 13)]

请注意,当序列长度相等时,zip() 是可逆的。

>>> a = (1, 2, 3)
>>> b = (4, 5, 6)
>>> x = zip(a, b)
>>> y = zip(*x) # alternatively, apply(zip, x)
>>> z = zip(*y) # alternatively, apply(zip, y)
>>> x
[(1, 4), (2, 5), (3, 6)]
>>> y
[(1, 2, 3), (4, 5, 6)]
>>> z
[(1, 4), (2, 5), (3, 6)]
>>> x == z
1

当序列长度不相等时,无法以这种方式反转 zip。

参考实现

这是一个参考实现,使用 Python 编写的 zip() 内置函数。在最终批准后,它将被 C 实现替换。

def zip(*args):
    if not args:
        raise TypeError('zip() expects one or more sequence arguments')
    ret = []
    i = 0
    try:
        while 1:
            item = []
            for s in args:
                item.append(s[i])
            ret.append(tuple(item))
            i = i + 1
    except IndexError:
        return ret

BDFL 声明

注意:BDFL 指的是 Guido van Rossum,Python 的终身仁慈独裁者。

  • 函数的名称。本 PEP 的早期版本包含一个开放问题列表,其中列出了 20 多个提议的 zip() 替代名称。由于没有压倒性更好的选择,BDFL 非常喜欢 zip(),因为它具有 Haskell[2] 遗产。有关替代方案列表,请参阅本 PEP 的 1.7 版。
  • zip() 应为内置函数。
  • 可选填充。本 PEP 的早期版本提出了一个可选的 pad 关键字参数,当参数序列长度不相等时使用。这类似于 map(None, ...) 语义,但用户可以指定填充对象。BDFL 拒绝了这一点,而是始终截断到最短序列,因为遵循 KISS 原则。如果确实需要,以后更容易添加。如果不需要,将来也无法删除它。
  • 惰性求值。本 PEP 的早期版本提出 zip() 返回一个使用 __getitem__() 协议执行惰性求值的内置对象。BDFL 坚决拒绝了这一点,而是选择返回一个真正的 Python 列表。如果将来需要惰性求值,BDFL 建议添加一个 xzip() 函数。
  • 无参数的 zip()。BDFL 非常喜欢抛出 TypeError 异常。
  • 带一个参数的 zip()。BDFL 非常喜欢返回一个 1 元组列表。
  • 内部和外部容器控制。本 PEP 的早期版本包含关于某些人想要的功能的相当长的讨论,即能够控制内部和外部容器类型(在本 PEP 版本中,它们分别是元组和列表)。鉴于简化的 API 和实现,此详细说明被拒绝。有关更详细的分析,请参阅本 PEP 的 1.7 版。

随后对 zip() 的更改

在 Python 2.4 中,无参数的 zip() 被修改为返回一个空列表,而不是抛出 TypeError 异常。原始行为的基本原理是,参数的缺失被认为表明编程错误。但是,这种想法没有预料到 zip() 与 * 运算符一起用于解包可变长度参数列表的使用。例如,zip 的逆可以定义为:unzip = lambda s: zip(*s)。这种转换还定义了矩阵转置或表格的行/列交换(表格定义为元组列表)。当读取数据文件时,将记录作为行,字段作为列,后者转换通常用于读取数据文件。例如,代码

date, rain, high, low = zip(*csv.reader(file("weather.csv")))

重新排列列数据,以便每个字段都被收集到单独的元组中,以便于循环和汇总。

print "Total rainfall", sum(rain)

如果 zip(*[]) 被视为允许的情况而不是异常,则 zip(*args) 更容易编码。当数据从空情况构建或递归到空情况(没有记录)时,这尤其有用。

看到这种可能性后,BDFL(带着一些疑虑)同意更改 Py2.4 的行为。

其他更改

  • 上面讨论的 xzip() 函数在 Py2.3 中的 itertools 模块中实现了,名为 itertools.izip()。此函数提供惰性行为,每次消耗单个元素并产生一个单个元组。“即时”样式节省了内存,并且比基于列表的对应项 zip() 运行速度更快。
  • itertools 模块还添加了 itertools.repeat()itertools.chain()。这些工具可以一起使用来用 None 填充序列(以匹配 map(None, seqn) 的行为)。
    zip(firstseq, chain(secondseq, repeat(None)))
    

参考文献

Greg Wilson 向一些计算机科学研究生提出的关于提议的语法的调查问卷https://pythonlang.cn/pipermail/python-dev/2000-July/013139.html


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

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