[Python-Dev] PEP 380 (yield from a subgenerator) comments

P.J. Eby pje at telecommunity.com
Fri Mar 27 05:14:01 CET 2009


At 10:39 PM 3/26/2009 -0500, Guido van Rossum wrote:
>That +0 could turn into a +1 if there was a way to flag this as an
>error (at runtime), at least if the return is actually executed:
>
>def g():
>     yield 42
>     return 43
>
>for x in g():
>     print x    # probably expected to print 42 and then 43
>
>Perhaps the exception used in this case could be a different exception
>than StopIteration? Regular iteration could either just pass this
>exception through or explicitly check for it (a single pointer
>comparison could usually suffice), depending on whether it would be a
>subclass of StopIteration.

Could we at least have some syntax like 'return from yield with 43', 
to distinguish it from a regular return, clarify that it's returning 
a value to a yield-from statement, and emphasize that you need a 
yield-from to call it?

If it doesn't have some sort of variant syntax, the error message for 
the return exception is going to need to be rather verbose in order 
to be clear.  However, if there is a variant syntax, then an error 
message like "'return from yield' without 'yield from'" might be 
clear enough, and we can keep the current error for returning values 
in generators.

That way, the paired special syntax is clearly identifiable as 
coroutine/microthread control flow, in a way that's both TOOOWTDI and EIBTI.

One remaining quirk or missing piece: ISTM there needs to be a way to 
extract the return value without using a yield-from statement.  I 
mean, you could write a utility function like:

    def unyield(geniter):
        try:
            while 1: geniter.next()
        except GeneratorReturn as v:
            return v.value

OTOH, I suppose this function is still a trampoline, just one that 
doesn't actually do anything except return an eventual exit value.  I 
suppose you could do a slightly improved one thus:

     def unyield(geniter, value=None, func=lambda v: v)
        try:
            while 1: value=func(geniter.send(value))
        except GeneratorReturn as v:
            return v.value

And drop it into itertools or some such.  It's sort of like an 
all-purpose map/reduce for generators, so that all you need to do is 
pass in a function to do whatever processing you need (e.g. I/O 
waiting) on the values yielded.  You could also use another 
generator's send() method as the function passed in, in which case 
you'd basically have a pair of coroutines...  and whichever returned 
a value first would end up as the return value of the overall 
function.  That'd probably be pretty useful for the sort of simple 
(non I/O) coroutines Greg seems to have in mind.

Or, these could just be examples in the PEP, I suppose.  They're not 
terribly difficult to write...  but then I might be biased since I've 
written a ridiculous number of coroutine trampolines for Python 
generators over the last how-many-ever years Python has had generators.



More information about the Python-Dev mailing list