On Tue, Nov 25, 2014 at 9:59 AM, Guido van Rossum email@example.com wrote:
On Tue, Nov 25, 2014 at 9:47 AM, Chris Barker firstname.lastname@example.org wrote:
[...] I like how it support my goal of keeping the behavior of iterator classes and generators consistent.
This is a great summary of the general confusion I am trying to clear up. The behavior of all types of iterators (including generators) from the *caller's* perspective is not in question and is not changing. It is very simple: you call next(it) (or it.__next__(), and it returns either the next value or raises StopIteration (and any other exception is, indeed, an exception).
From the POV of *implementor*, iterator classes and generators have never been at all alike -- one uses *return* to produce a new value, the other uses *yield*. Returning without a value from a __next__() method is seen as producing a None value by the caller; returning from a generator is translated into a StopIteration which will be interpreted by the caller as the end of series.
Once you start nesting these things, the distinction between "implementor" and "caller" gets mingled. And I think this is all about how nested generators behave, yes?
If I am implementing a iterator of some sort (generator function or iterator class), and I call next() inside my code, then I am both a implementor and caller. And if I'm also writing helper functions, then I need to know about how StopIteration will be handled, and it will be handled a bit differently by generators and iterator classes.
But not a big deal, agreed, probably a much smaller deal that all the other stuff you'd better understand to write this kind of code anyway.