Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python PEP479 Change StopIteration handling inside generators

Could someone help me understand what PEP479 is about? I was reading the doc and couldn't get my head around it.

The abstract says:

This PEP proposes a change to generators: when StopIteration is raised inside a generator, it is replaced it with RuntimeError. (More precisely, this happens when the exception is about to bubble out of the generator's stack frame.)

So for example, does a loop like so still work?

it = iter([1,2,3])
try:
    i = next(it)
    while True:
        i = next(it)
except StopIteration:
    pass

Or does it mean that if I have a generator definition like so:

def gen():
    yield from range(5)
    raise StopIteration

the StopIteration is going to be replaced with RuntimeError?

I would really appreciate if someone could shed some light on this.

like image 876
Jacques Gaudin Avatar asked Jun 08 '16 15:06

Jacques Gaudin


People also ask

How do you fix StopIteration in Python?

Iterator in Python uses the two methods, i.e. iter() and next(). The next() method raises an StopIteration exception when the next() method is called manually. The best way to avoid this exception in Python is to use normal looping or use it as a normal iterator instead of writing the next() method again and again.

Does yield raise StopIteration?

A yield point is reached, and the yielded value is returned. The frame is returned from; StopIteration is raised. An exception is raised, which bubbles out.

How do Python generators work internally?

A Python generator is a function that produces a sequence of results. It works by maintaining its local state, so that the function can resume again exactly where it left off when called subsequent times. Thus, you can think of a generator as something like a powerful iterator.

What does next () do in a generator Python?

The next() function returns the next item in an iterator. You can add a default return value, to return if the iterable has reached to its end.


1 Answers

Your first loop should still work -- StopIteration will still be raised when a generator is exhausted.

The difference is that there was ambiguity when a StopIteration was raised in a generator. Did it get raised (implicitly) because the generator ran out of things to yield -- Or did it get raised because a delegate generator ran out of things to yield (maybe due to a next call) and the exception wasn't handled properly? PEP-0479 tries to address that ambiguity. Now if you get a StopIteration, it means that the generator you are working with ran out items to yield. Said another way, it means that a delegate generator didn't get mis-handled when running out of items.

To support this change, your generator should return instead of raising StopIteration explicitly.

def gen():
    yield from range(5)
    return

Here's what happens if you try it with the StopIteration and generator_stop enabled (which will become the default when python3.7 comes around):

>>> from __future__ import generator_stop
>>> def gen():
...     yield from range(5)
...     raise StopIteration
... 
>>> list(gen())
Traceback (most recent call last):
  File "<stdin>", line 3, in gen
StopIteration

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: generator raised StopIteration
like image 159
mgilson Avatar answered Sep 27 '22 22:09

mgilson