Nick Coghlan wrote:
Jacob Holm wrote:
def averager(): count = 0 sum = 0 while 1: try: val = (yield) except GeneratorExit: return sum/count else: sum += val count += 1
avg = averager() avg.next() # start coroutine avg.send(1.0) avg.send(2.0) print avg.close() # prints 1.5
But that's not how it works, unless you're asking Greg to change the PEP to allow that. I am most definitely asking Greg to change the PEP to allow that. Specifically I am asking for a clarification in the PEP of how GeneratorReturn/StopIteration is handled in close(), and requesting that we define close() to return the value rather than letting the GeneratorReturn be raised.
And while it looks cute a single layer deep like that, it goes wrong as soon as you consider the fact that if you get a GeneratorReturn exception on close(), you *don't know* if that result came from the outer iterator.
Using option #4 from the list I made of possible finalization strategies which is what most of you seemed to prefer, and assuming that close catches GeneratorReturn/StopIteration you *can* be sure. There is no way it could come from anywhere else in the yield-from stack. Of course you can raise the exception manually or call a function that does, but that is crazy code...
A better way to write that averager would be:
def averager(): # Works for Python 2.5+ count = 0 sum = 0 while 1: val = (yield) if val is None: yield sum/count break sum += val count += 1
avg = averager() avg.next() # start coroutine avg.send(1.0) avg.send(2.0) print avg.send(None)
1.5
Yes, I am aware that you can pass special values to send. I find this version less appealing than mine for at least the following reasons: 1. You need to use a magic "stop" value (in this case None). 2. You are using the same "send" method for two radically different purposes on the same object. 3. You need a separate "close" step to clean up afterwards (which you forgot). 4. You use "yield" for different purposes at different times (mostly input, then a single output). 5. I find the control flow in mine simpler to understand. It explicitly mentions GeneratorExit, and immediately returns. Yours must check for the magic "stop" value, yield a result, then break/return. #1,2,3 makes the API of the averager object more complex than it needs to be. #4 is generally considered ugly, but is sometimes necessary. #5 is just a personal preference. Best regards - jacob