PEP 322 – 反向迭代
- 作者:
- Raymond Hettinger <python at rcn.com>
- 状态:
- 最终
- 类型:
- 标准轨迹
- 创建:
- 2003-09-24
- Python 版本:
- 2.4
- 历史记录:
- 2003-09-24
摘要
此提案旨在添加一个内置函数来支持对序列的反向迭代。
动机
对于可索引对象,当前反向迭代方法容易出错、不自然且可读性不高。
for i in xrange(n-1, -1, -1):
print seqn[i]
另一种当前方法是在迭代之前反转列表。这种技术会浪费计算机周期、内存和代码行。
rseqn = list(seqn)
rseqn.reverse()
for value in rseqn:
print value
扩展切片是第三种方法,它最大程度地减少了代码开销,但对内存效率、美观或清晰度没有帮助。
反向迭代比正向迭代少得多,但它在实践中确实会定期出现。请参见下面的实际应用案例。
提案
添加一个名为reversed()的内置函数,该函数在支持__getitem__()和__len__()的序列对象上生成反向迭代器。
上面的示例简化为
for i in reversed(xrange(n)):
print seqn[i]
for elem in reversed(seqn):
print elem
核心思想是最清晰、最不容易出错的反向迭代方式是指定正向方向,然后说reversed。
实现可以像这样简单
def reversed(x):
if hasattr(x, 'keys'):
raise ValueError("mappings do not support reverse iteration")
i = len(x)
while i > 0:
i -= 1
yield x[i]
不需要语言语法更改。该提案完全向后兼容。
C 实现和单元测试位于:https://bugs.python.org/issue834422
BDFL 宣告
该 PEP 已有条件接受 Py2.4。条件意味着如果发现该函数无用,可以在 Py2.4b1 之前将其删除。
替代方法名称
- reviter – Jeremy Fincher 的建议与 iter() 的用法一致
- ireverse – 使用 itertools 的命名约定
- inreverse – 除了我之外,似乎没有人喜欢这个
名称reverse 不是候选,因为它重复了列表的 list.reverse() 的名称,该名称会修改底层列表。
讨论
反对采用该 PEP 的理由是希望内置函数数量保持较少。这需要权衡拥有它的简单性和便利性,而不是将其隐藏在其他命名空间中。
实际应用案例
以下是一些来自标准库的反向迭代示例,以及为什么需要反向迭代的说明
- atexit.exit_handlers() 使用
while _exithandlers: func, targs, kargs = _exithandlers.pop() . . .
在此应用中,需要弹出,因此新的函数不会有任何帮助。
- heapq.heapify() 使用
for i in xrange(n//2 - 1, -1, -1)
因为更高级别的排序更容易从更低级别的排序对中形成。此算法的正向版本是可能的;但是,这会使堆代码的其余部分复杂化,因为堆代码以相反的方向遍历底层列表。替换代码for i in reversed(xrange(n//2))
清楚地说明了所涵盖的范围以及需要多少次迭代。 - mhlib.test() 使用
testfolders.reverse(); for t in testfolders: do('mh.deletefolder(%s)' % `t`)
需要反向迭代是因为在迭代期间底层列表的尾部会被修改。
- platform._dist_try_harder() 使用
for n in range(len(verfiles)-1,-1,-1)
因为循环从verfiles中删除选定的元素,但需要保持列表的其余部分完好无损以供进一步迭代。 - random.shuffle() 使用
for i in xrange(len(x)-1, 0, -1)
因为该算法最容易理解为从不断减少的池中随机选择元素。事实上,该算法可以在正向运行,但不太直观,在文献中很少以这种方式呈现。替换代码for i in reversed(xrange(1, len(x)))
更容易进行视觉验证。 - rfc822.Message.__delitem__() 使用
list.reverse() for i in list: del self.headers[i]
需要反向迭代是因为在迭代期间底层列表的尾部会被修改。
被拒绝的替代方案
提交了几个变体,试图通过将可迭代对象运行到完成、保存结果,然后返回对结果进行反向迭代的迭代器来将reversed()应用于所有可迭代对象。虽然满足了一些关于完全通用性的概念,但将输入运行到末尾与使用迭代器的目的相违背。此外,如果底层迭代器是无限的,则会发生一个小灾难。
将该函数放在另一个模块中或将其附加到类型对象中并不在考虑范围之内。与它的同类zip()和enumerate()一样,该函数需要在日常编程中直接访问。每个函数都解决了一个基本的循环问题:同步迭代、循环计数和反向迭代。需要某种形式的点访问会影响其简单性、日常实用性和可访问性。它们是核心循环结构,独立于任何一个应用领域。
版权
本文档已置于公有领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0322.rst
最后修改时间:2023-09-09 17:39:29 GMT