PEP 3142 – 为生成器表达式添加“while”子句
- 作者:
- Gerald Britton <gerald.britton at gmail.com>
- 状态:
- 已拒绝
- 类型:
- 标准跟踪
- 创建日期:
- 2009年1月12日
- Python 版本:
- 3.0
- 发布历史:
- 决议:
- Python-Dev 消息
摘要
本 PEP 提议增强生成器表达式,添加一个“while”子句来补充现有的“if”子句。
基本原理
生成器表达式(PEP 289)是一种简洁的方法,用于将动态生成的对象提供给列表推导式(PEP 202)。当前的生成器表达式允许使用“if”子句来过滤返回的对象,只保留满足某些标准的那些。然而,由于“if”子句会针对每个可能返回的对象进行求值,在某些情况下,可能会出现某个点之后的所有对象都被拒绝的情况。例如
g = (n for n in range(100) if n*n < 50)
这等同于使用生成器函数(PEP 255)
def __gen(exp):
for n in exp:
if n*n < 50:
yield n
g = __gen(iter(range(10)))
将产生 0, 1, 2, 3, 4, 5, 6 和 7,但也会考虑 8 到 99 的数字并全部拒绝,因为在这个范围内,n*n >= 50。允许使用“while”子句将允许短路冗余的测试
g = (n for n in range(100) while n*n < 50)
也将产生 0, 1, 2, 3, 4, 5, 6 和 7,但在 8 处停止,因为条件(n*n < 50)不再为真。这等同于生成器函数
def __gen(exp):
for n in exp:
if n*n < 50:
yield n
else:
break
g = __gen(iter(range(100)))
目前,为了达到相同的结果,需要要么编写上述那样的生成器函数,要么使用 itertools 中的 takewhile 函数
from itertools import takewhile
g = takewhile(lambda n: n*n < 50, range(100))
takewhile 代码实现了与提议语法相同的结果,尽管方式更冗长(有些人会说“不够优雅”)。此外,takewhile 版本需要额外的函数调用(上面的示例中的 lambda),并伴随着相应的性能损失。一个简单的测试表明
for n in (n for n in range(100) if 1): pass
比...性能好约 10%
for n in takewhile(lambda n: 1, range(100)): pass
尽管它们实现了相似的结果。(第一个示例使用生成器;takewhile 是一个迭代器)。如果实现方式相似,“while”子句的性能应该与今天的“if”子句的性能大致相同。
读者可能会问,“if”和“while”子句是否应该互斥。有一些很好的例子表明,在某些时候两者都可以发挥优势。例如
p = (p for p in primes() if p > 100 while p < 1000)
应该返回 100 到 1000 之间找到的素数,假设我有一个 primes() 生成器,它产生素数。
为生成器表达式添加“while”子句在保持紧凑形式的同时,增加了一个有用的短路表达式的功能。
致谢
Raymond Hettinger 于 2002 年 1 月首次提出了生成器表达式的概念。
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-3142.rst