为什么这些生成器表达式的行为不同?
Why do these generator expressions behave differently?
这两个代码片段仅在构建列表的方式上有所不同。一个使用 []
,另一个使用 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.
这种行为的规则是什么?
参考PEP479,里面说
The interaction of generators and StopIteration is currently somewhat
surprising, and can conceal obscure bugs. An unexpected exception
should not result in subtly altered behaviour, but should cause a
noisy and easily-debugged traceback. Currently, StopIteration raised
accidentally inside a generator function will be interpreted as the
end of the iteration by the loop construct driving the generator.
(强调我的)
因此 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提出的行为如下
If a StopIteration
is about to bubble out of a generator frame, it is
replaced with RuntimeError
, which causes the next()
call (which
invoked the generator) to fail, passing that exception out. From then
on it's just like any old exception.
Python 3.5 添加了可以使用
启用的 generator_stop
feature
from __future__ import generator_stop
此行为将在 Python 3.7.
中成为默认行为
这两个代码片段仅在构建列表的方式上有所不同。一个使用 []
,另一个使用 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.
这种行为的规则是什么?
参考PEP479,里面说
The interaction of generators and StopIteration is currently somewhat surprising, and can conceal obscure bugs. An unexpected exception should not result in subtly altered behaviour, but should cause a noisy and easily-debugged traceback. Currently, StopIteration raised accidentally inside a generator function will be interpreted as the end of the iteration by the loop construct driving the generator.
(强调我的)
因此 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提出的行为如下
If a
StopIteration
is about to bubble out of a generator frame, it is replaced withRuntimeError
, which causes thenext()
call (which invoked the generator) to fail, passing that exception out. From then on it's just like any old exception.
Python 3.5 添加了可以使用
启用的generator_stop
feature
from __future__ import generator_stop
此行为将在 Python 3.7.
中成为默认行为