Delgan
Delgan

Reputation: 19617

Why does nested empty generator early exit without raising error?

I am facing a strange behavior with nested generators.

def empty_generator():
    for i in []:
        yield

def gen():
    next(empty_generator())
    print("This is not printed, why?")
    yield

list(gen())  # No Error

next(empty_generator())  # Error

I would expect the gen() function to raises an error, as I am calling next() around an empty generator. But this is not the case, the functions is leaving from nowhere, without raising or printing anything.

That seems to violate the principle of least astonishment, isn't it?

Upvotes: 2

Views: 47

Answers (2)

chepner
chepner

Reputation: 531055

Technically, you don't have an error; you have an uncaught StopIteration exception, which is used for flow control. The call to list, which takes an arbitrary iterable as its argument, catches the exception raised by gen for you.

for loops work similarly; every iterator raises StopIteration at the end, but the for loop catches it and ends in response.

Put another way, the consumer of an iterable is responsible for catching StopIteration. When gen calls next, it lets the exception bubble up. The call to list catches it, but you don't when you call next explicitly.


Note that PEP-479 changes this behavior. Python 3.5 provides the new semantics via __future__, Python 3.6 makes provides a deprecation warning, and Python 3.7 (due out Summer 2018) completes the transition. I refer the reader to the PEP itself for further details.

Upvotes: 2

ForceBru
ForceBru

Reputation: 44838

Once an iterator reaches its end, it raises StopIteration which... stops the iteration, so list(gen()) constructs an empty list.

Upvotes: 1

Related Questions