[Python-ideas] Generators are iterators

Terry Reedy tjreedy at udel.edu
Sun Dec 14 18:48:22 CET 2014


On 12/14/2014 10:02 AM, Nick Coghlan wrote:

> If you are teaching someone to write explicit __next__ methods, here
> are the things you *absolutely* need to teach them:
>
> 1. Values returned from the method are produced as values in the iterator
> 2. Raising StopIteration terminates the iterator
> 3. If invoked via "yield from", the argument to StopIteration is the
> result of the "yield from" expression (default: None)

I had forgotten this apparently important point.  After re-reading the 
somewhat confusing 'Yield expressions' doc, I came up with this example.

class subc:
     def __iter__(self): return self
     def __init__(self): self.i, self.it = -1, (0,1,2)
     def __next__(self):
         if self.i < 2:
             self.i += 1
             return self.it[self.i]
         else:
             raise StopIteration('subc done')

def subg():
     for i in range(3):
         yield i
     return 'sub done'

def sup(sub):
     print((yield from sub()))

print(list(sup(subc)))
print(list(sup(subg)))

it = subg()
result = []
try:
     while True:
         result.append(next(it))
except StopIteration as e:
     print(e.args[0])
print(result)

 >>>
sub done
[0, 1, 2]
sub done
[0, 1, 2]
sub done
[0, 1, 2]

Pep 380 not only adds 'yield from', but also a mechanism for *any* 
iterator to optionally pass a terminal message back to *any* direct user 
that looks for the message. I don't know how I might use this feature, 
which is why I more or less ignored it at the time it was introduced, 
but it is part of asyncio's use of 'yield from'.

For statements ignore the return message, so this is one possible reason 
to use while and explicit next inside try.

> 4. If delegating to a function via a function call, points 2 & 3 also
> apply to that subfunction (this includes the builtin next() function)
>
> If you are instead teaching someone to write generator functions, here
> are the things you *absolutely* need to teach them:
>
> 1. Values passed to yield expressions are produced as values in the iterator
> 2. Returning from the frame terminates the iterator
> 3. If invoked via "yield from", the return value of the generator
> frame is the result of the "yield from" expression (default: None)

Or the return value can be accessed by catching the resulting 
StopIteration of next().

> 4. If delegating to a subgenerator (rather than to an arbitrary
> iterator) via a "yield from" expression, then next(), send() and
> throw() are passed to that subgenerator until it terminates, at which
> point execution resumes in the parent generator frame (either
> producing a value from the yield from expression, or raising an
> exception if the subgenerator failed)

-- 
Terry Jan Reedy



More information about the Python-ideas mailing list