max max - 2 months ago 8
Python Question

Why exhausted generators raise StopIteration more than once?

Why is it that when an exhausted generator is called several times,

StopIteration
is raised every time, rather than just on the first attempt? Aren't subsequent calls meaningless, and indicate a likely bug in the caller's code?

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


This also results in this behavior when someone accidentally uses an expired generator:

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)


If second and later attempts to call an exhausted generator raised a different exception, it would have been easier to catch this type of bugs.

Perhaps there's an important use case for calling exhausted generators multiple times and getting
StopIteration
?

Answer

Perhaps there's an important use case for calling exhausted generators multiple times and getting StopIteration?

There is, specifically, when you want to perform multiple loops on the same iterator. Here's an example from the itertools docs that relies on this behavior:

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)
Comments