为什么当多次调用耗尽的生成器时,每次都会引发StopIteration
,而不是仅仅是第一次尝试?后续调用是否毫无意义,并指出调用者代码中可能存在错误?
def gen_func():
yield 1
yield 2
gen = gen_func()
next(gen)
next(gen)
next(gen) # StopIteration as expected
next(gen) # why StopIteration and not something to warn me that I'm doing something wrong
当有人意外使用过期的生成器时,这也会导致此行为:
def do_work(gen):
for x in gen:
# do stuff with x
pass
# here I forgot that I already used up gen
# so the loop does nothing without raising any exception or warning
for x in gen:
# do stuff with x
pass
def gen_func():
yield 1
yield 2
gen = gen_func()
do_work(gen)
如果第二次和以后尝试调用耗尽的生成器引发了一个不同的异常,那么捕获这种类型的错误会更容易。
也许有一个重要的用例是多次调用耗尽的生成器并获得StopIteration
?
答案 0 :(得分:4)
它是迭代协议的一部分:
一旦迭代器的
__next__()
方法引发StopIteration,它就必须 在接下来的电话中继续这样做。没有的实现 服从此财产被视为破产。
来源:https://docs.python.org/3/library/stdtypes.html#iterator-types
答案 1 :(得分:3)
也许有一个重要的用例是多次调用耗尽的生成器并获得
StopIteration
?
具体来说,当您想在同一个迭代器上执行多个循环时。以下是依赖于此行为的itertools
文档中的示例:
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return zip_longest(*args, fillvalue=fillvalue)
答案 2 :(得分:0)
这是包装器的一种实现,只要多次停止StopIteration,它就会引发错误,因为已经noted by VPfB,这被认为是坏的
#!/usr/bin/env python3.8
from typing import TypeVar, Iterator
"""
https://docs.python.org/3/library/stdtypes.html#iterator-types
This is considered broken by the iterator protocol, god knows why
"""
class IteratorExhaustedError(Exception):
"""Exception raised when exhausted iterators are ``next``d"""
T = TypeVar("T")
class reuse_guard(Iterator[T]):
"""
Wraps an iterator so that StopIteration is only raised once,
after that, ``IteratorExhaustedError`` will be raised to detect
fixed-size iterator misuses
"""
def __init__(self, iterator: Iterator[T]):
self._iterated: bool = False
self._iterator = iterator
def __next__(self) -> T:
try:
return next(self._iterator)
except StopIteration as e:
if self._iterated:
raise IteratorExhaustedError(
"This iterator has already reached its end")
self._iterated = True
raise e
def __iter__(self) -> Iterator[T]:
return self
示例:
In [48]: iterator = reuse_guard(iter((1, 2, 3, 4)))
In [49]: list(iterator)
Out[49]: [1, 2, 3, 4]
In [50]: list(iterator)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-47-456650faec86> in __next__(self)
19 try:
---> 20 return next(self._iterator)
21 except StopIteration as e:
StopIteration:
During handling of the above exception, another exception occurred:
IteratorExhaustedError Traceback (most recent call last)
<ipython-input-50-5070d0fe4365> in <module>
----> 1 list(iterator)
<ipython-input-47-456650faec86> in __next__(self)
21 except StopIteration as e:
22 if self._iterated:
---> 23 raise IteratorExhaustedError(
24 "This iterator has already reached its end")
25 self._iterated = True
IteratorExhaustedError: This iterator has already reached its end
编辑: 在回顾有关迭代器协议的文档之后,在我看来,表明不再继续提高StopIteration的迭代器应视为已被破坏的目的更多地是针对 yield values 而不是引发异常的迭代器,在这种情况下,可以更清楚地知道,迭代器一旦用尽,就不应使用。这只是我的解释思想。