[Python-ideas] Possible PEP 380 tweak
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
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
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.
count = tally = 0
tally += yield
count += 1
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.
gt = gtally()
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!
gavg = gtally_averages()
for x in range(100):
It may be that a lot of pre-existing generators will already work with
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.
More information about the Python-ideas