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
比
for n in takewhile(lambda n: 1, range(100)): pass
的性能大约高出 10%,尽管它们实现的结果相似。(第一个示例使用生成器;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
最后修改时间: 2023-09-09 17:39:29 GMT