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() # works in this frame.
yield from except <exception> #Delegate until <exception>
value = next() # 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