Reputation: 19617
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
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
Reputation: 44838
Once an iterator reaches its end, it raises StopIteration
which... stops the iteration, so list(gen())
constructs an empty list.
Upvotes: 1