为什么这些生成器表达式的行为不同?

时间:2015-07-11 19:50:40

标签: python list-comprehension generator-expression

这两个代码片段的区别仅在于列表的构造方式。一个使用[],另一个使用list()

这个消耗了iterable,然后引发StopIteration

>>> try:
...     iterable = iter(range(4))
...     while True:
...         print([next(iterable) for _ in range(2)])
... except StopIteration:
...     pass
...
[0, 1]
[2, 3]

这个消耗了iterable并循环永久打印空列表。

>>> try:
...     iterable = iter(range(4))
...     while True:
...         print(list(next(iterable) for _ in range(2)))
... except StopIteration:
...     pass
...
[0, 1]
[2, 3]
[]
[]
[]
etc.

此行为有哪些规则?

1 个答案:

答案 0 :(得分:3)

请参阅PEP479,其中说明了

  

发电机和StopIteration的相互作用目前有所不同   令人惊讶的是,可以隐藏晦涩的错误。意外的例外   不应该导致微妙改变的行为,但应该导致一个   嘈杂且易于调试的追溯。 目前,StopIteration已经提出   意外地在发电机功能内将被解释为   循环结构驱动发生器的迭代结束

(强调我的)

因此list的构造函数遍历传递的生成器表达式,直到引发StopIteration错误(通过调用next(iterable)而没有第二个参数)。另一个例子:

def f():
    raise StopIteration # explicitly

def g():
    return 'g'

print(list(x() for x in (g, f, g))) # ['g']
print([x() for x in (g, f, g)]) # `f` raises StopIteration

另一方面,* comprehension在将StopIteration传播给调用者时的工作方式不同。

链接的PEP提出的行为如下

  

如果StopIteration即将从生成器框架中冒出来,那就是   替换为RuntimeError,这会导致next()调用(其中)   调用生成器失败,将该异常传递出去。从那时起   就像任何旧例外一样。

Python 3.5添加了可以使用

启用的generator_stop feature
from __future__ import generator_stop

此行为在Python 3.7中将是默认行为。