[Python-ideas] Possible PEP 380 tweak

Ron Adam rrr at ronadam.com
Sat Oct 30 08:42:18 CEST 2010



On 10/29/2010 10:09 PM, Greg Ewing wrote:
> Guido van Rossum wrote:
>
>> This seems to be the crux of your objection. But if I look carefully
>> at the expansion in the current version of PEP 380, I don't think this
>> problem actually happens: If the outer generator catches
>> GeneratorExit, it closes the inner generator (by calling its close
>> method, if it exists) and then re-raises the GeneratorExit:
>
> Yes, but if you want close() to cause the generator to finish
> normally, you *don't* want that to happen. You would have to
> surround the yield-from call with a try block to catch the
> GeneratorExit, and even then you would lose the return value
> from the inner generator, which you're probably going to
> want.


Ok, after thinking about this for a while, I think the "yield from" would 
be too limited if it could only be used for consumers that must run until 
the end. That rules out a whole lot of pipes, filters and other things that 
consume-some, emit-some, consume-some_more, and emit-some_more.


I think I figured out something that may be more flexible and insn't too 
complicated.

The trick is how to tell the "yield from" to stop delegating on a 
particular exception.  (And be explicit about it!)


     # Inside a generator or sub-generator.
     ...

     next(<my_gen>)    # works in this frame.

     yield from <my_gen> except <exception>  #Delegate until <exception>

     value = next(<my_gen>)    # works in this frame again.

     ...

The explicit "yield from .. except" is easier to understand.  It also 
avoids the close and return issues.  It should be easier to implement as 
well.  And it doesn't require any "special" framework in the parent 
generator or the delegated sub-generator to work.


Here's an example.

# I prefer to use a ValueRequest exception, but someone could use
# StopIteration or GeneratorExit, if it's useful for what they
# are doing.

class ValueRequest(Exception): pass


# A pretty standard generator that emits
# a total when an exception is thrown in.
# It doesn't need anything special in it
# so it can be delegated.

def gtally():
     count = tally = 0
     try:
         while 1:
             tally += yield
             count += 1
     except ValueRequest:
         yield count, tally


# An example of delegating until an Exception.
# The specified "exception" is not sent to the sub-generator.
# I think explicit is better than implicit here.

def gtally_averages():
     gt = gtally()
     next(gt)
     yield from gt except ValueRequest     #Catches exception
     count, tally = gt.throw(ValueRequest)    #Get tally
     yield tally / count


# This part also already works and has no new stuf in it.
# This part isn't aware of any delegating!

def main():
     gavg = gtally_averages()
     next(gavg)
     for x in range(100):
         gavg.send(x)
     print(gavg.throw(ValueRequest))

main()


It may be that a lot of pre-existing generators will already work with 
this. ;-)

You can still use 'yield from <gen>" to delegate until <gen> ends.  You 
just won't get a value in the same frame <gen> was used in.  The parent may 
get it instead. That may be useful in it self.

Note: you *can't* put the yield from inside a try-except and do the same 
thing.  The exception would go to the sub-generator instead.  Which is one 
of the messy things we are trying to avoid doing.

Cheers,
    Ron



More information about the Python-ideas mailing list