[Python-Dev] PEP 479: Change StopIteration handling inside generators

Ron Adam ron3200 at gmail.com
Sat Nov 22 20:49:00 CET 2014



On 11/22/2014 08:31 AM, Nick Coghlan wrote:
>
> On 22 Nov 2014 02:51, "Antoine Pitrou" <solipsis at pitrou.net
> <mailto:solipsis at pitrou.net>> wrote:
>  >
>  > On Fri, 21 Nov 2014 05:47:58 -0800
>  > Raymond Hettinger <raymond.hettinger at gmail.com
> <mailto:raymond.hettinger at gmail.com>> wrote:
>  > >
>  > > Another issue is that it breaks the way I and others have taught for
> years that generators are a kind of iterator (an object implementing the
> iterator protocol) and that a primary motivation for generators is to
> provide a simpler and more direct way of creating iterators.  However,
> Chris explained that, "This proposal causes a separation of generators and
> iterators, so it's no longer possible to pretend that they're the same
> thing."  That is a major and worrisome conceptual shift.
>  >
>  > I agree with Raymond on this point.
>
> A particularly relevant variant of the idiom is the approach of writing
> "__iter__" directly as a generator, rather than creating a separate custom
> iterator class. In that context, the similarities between the __iter__
> implementation and the corresponding explicit __next__ implementation is a
> beneficial feature.
>
> I'm definitely coming around to the point of view that, even if we wouldn't
> design it the way it currently works given a blank slate, the alternative
> design doesn't provide sufficient benefit to justify the cost of changing
> the behaviour & getting people to retrain their brains.

This all seems more complex than it should be to me.  The way I tend to 
think about it is simply "for" loops in one form or another, catch 
StopIteration.  So if you iterate an iterator manually rather than using it 
as a "for" iterator, you need to catch StopIteration.

If we write a function to act as an iterator, like __next__, we need to 
raise StopIteration from somewhere in it, or let one bubble out from a 
generator if we are manually iterating it on each call... next(g). It's 
possible we may need to do either or both conditionally.  That could mean 
we need to think about refactoring some part of a program, but it doesn't 
mean Python needs to be fixed.

So the lines that split them isn't quite as clear cut as it may seem they 
should be.  That may just be a misplaced ideal.

Any time a StopIteration is raised.. either manually with "raise", or at 
the end of a generator, it should bubble up until a "for loop" iterating 
over that bit of code, catches it, or a try-except catches it, or fail 
loudly.  I think it does do this in normal generators, so I don't see an 
issue with how StopIteration works.


Which gets us back to generator expressions and comprehensions.

Let me know if I got some part of this wrong...   :-)

Comprehensions are used as a convenient way to create an object.  The 
expression parts of the comprehension define the *body* of a loop, so a 
StopIteration raised in it will bubble out.  As it would in any other case 
where it is raised in the body of a loop.

Generator exprssions on the other hand define the *iterator* to be used in 
a for loop. A StopIteration raised in it is caught by the for loop.

So they both work as they are designed, but they look so similar, it looks 
like one is broken.



It looks to me that there are three options...


OPTION 1:

Make comprehensions act more like generator expressions.

It would mean a while loop in the object creation point is converted to a 
for loop. (or something equivalent.)

Then both a comprehension and a generator expressions can be viewed as 
defining iterators, with the same behaviour, rather than comprehensions 
defining the body of the loop, which has the different but valid behaviour 
of StopIteration escaping.

This would make explaining them a *lot* easier as they become the same 
thing used in a different context, rather than two different things used in 
what appears to be similar contexts.


I think this fits with what Guido wants, but does so in a narrower scope, 
only effecting comprehensions.  StopIteration is less likely to leak out.

But it also allows the use of the stop() hack to raise StopIteration in 
comprehensions and terminate them early.  Currently it doesn't work as it 
does in generator expressions.

If the stop() hack works in both comprehensions and generator expressions 
the same way, then maybe we can view it as less of a hack.


OPTION 2:

Make generator expressions more like comprehensions.

This would mean StopIteration would bubble out of generator expressions as 
the person who posted the original topic on python ideas wanted.  And the 
stop hack would no longer work.

Both generator expressions and comprehensions could be viewed as supplying 
the body in a loop.  This is inconsistent with defining generators that act 
as iterators.  So I'm definitely -1 on this option.


OPTION 3:

Document the differences better.




Cheers,
    Ron
























More information about the Python-Dev mailing list