PEP 322 – 反向迭代
- 作者:
- Raymond Hettinger <python at rcn.com>
- 状态:
- 最终版
- 类型:
- 标准跟踪
- 创建日期:
- 2003年9月24日
- Python 版本:
- 2.4
- 发布历史:
- 2003年9月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
上次修改时间:2025-02-01 08:59:27 GMT