这是对Python的一个更改,它正逐步在python3.5和python3.7之间分阶段进行。细节在PEP 479中有解释,但我将试着给出一个简短的概述。在
问题是StopIteration异常正在从生成器函数中泄漏。看起来没问题,因为发出StopIteration是迭代器完成的信号。但它可能会导致重构生成器时出现问题。下面是一个显示问题的示例:
假设您有这个generator函数(在3.5之前的Python版本中可以正常工作,在那里它开始发出警告):def gen():
yield 1
yield 2
if True:
raise StopIteration
yield 3
yield 4
由于if的条件是真实的,生成器将在产生两个值(不产生3或{})后停止。但是,如果您试图重构函数的中间部分呢?如果将部件从yield 2移到yield 3到辅助生成器中,您将看到一个问题:
^{pr2}$
在此版本中,3将被跳过,但仍将生成4。这是因为yield from吃掉了在helper生成器函数中引发的StopIteration。它假设只有helper生成器应该停止,因此外部生成器继续运行。在
为了解决这个问题,Python开发人员决定改变生成器的工作方式。从Python3.7开始,从生成器函数中泄漏的StopIteration异常将被解释器更改为RuntimeError异常。如果要正常退出生成器,则需要使用return。此外,现在可以return生成函数中的值。该值将包含在生成器机器引发的StopIteration异常中,yield from表达式将计算为返回值。在
因此,上述生成器可以适当地重构为:def gen():
yield 1
if yield from refactored_helper():
return
yield 4
def refactored_helper():
yield 2
if True:
return True
yield 3
# like a normal function, a generator returns None if it reaches the end of the code
如果您现在想编写将来兼容的代码,您应该将from __future__ import generator_stop放在模块的顶部。然后,您需要跟踪泄漏StopIteration异常的地方,并用try和{}逻辑包装它们。对于您问题中的代码:from __future__ import generator_stop
from itertools import chain, islice
def grouper(iterable, n):
iterable = iter(iterable)
while True:
try:
peek = next(iterable)
except StopIteration:
return
yield chain((peek,), islice(iterable, n - 1))