4

这两个代码片段仅在构造列表的方式上有所不同。一用[],二用list()

这个消耗迭代然后引发StopIteration

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

这个消耗可迭代并永远循环打印空列表。

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

这种行为的规则是什么?

4

1 回答 1

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

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


链接的 PEP 提出的行为如下

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

Python 3.5 添加了可以使用启用的generator_stop功能

from __future__ import generator_stop

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

于 2015-07-11T19:56:07.663 回答