Guido van Rossum wrote:
On Thu, Apr 2, 2009 at 5:35 PM, Ron Adam <rrr@ronadam.com> wrote:
Jacob Holm wrote:
That might be the prevailing wisdom concerning GeneratorExit, at least partly based on the fact that the only way to communicate anything useful out of a closing generator is to raise another exception. Thinking a bit about coroutines, it would be nice to use "send" for the normal communication and "close" to shut it down and getting a final result. Example:
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
To do something similar today requires either a custom exception, or the use of special values to tell the generator to yield the result. I find this version a lot cleaner. This doesn't seem less cleaner than the above to me.
def averager(): sum = 0 count = 0 try: while 1: sum += yield count += 1 finally: yield sum / count
avg = averager() avg.next() avg.send(1.0) avg.send(2.0) print avg.next() # prints 1.5
But your version isn't clean -- it relies on "sum += yield" raising a TypeError when yield returns None (due to .next() being the same as .send(None)).
Something I noticed is that function and method calls use TypeError in cases where the argument count is miss matched. If there was a different exception for miss matched arguments and .next() sent no arguments, it could be rewritten in a somewhat cleaner way. def averager(): sum = 0 count = 0 try: while 1: sum += yield count += 1 except ArgumentCountError: yield sum / count avg = averager() avg.next() avg.send(1.0) avg.send(2.0) print avg.next() # prints 1.5 This seems to me that a different exception for miss-matched arguments might be useful in a more general way.
That's not to say I like Jacob's version that much, but I now understand his use case. I note that Dave Beazley works around this carefully in his tutorial (dabeaz.com/coroutines/) by using examples that produce output on stdout -- and much later, in his multitasking schedule example, his trampoline actually interprets yielding a value that is neither a SystemCall instance nor a generator as a return from a generator. (This is similar to the abuse that your example is giving yield, actually.) I'll have to ponder this more.
Yes, generators seem very limiting to me. Generators with more complex input and output requirements are bound to have added complexities to offset the limits of a single sequential i/o channel.
__________ PS. Somehow the headers in your email made my reply add this:
Python-Ideas <public-python-ideas-+ZN9ApsXKcEdnm+yROfE0A@ciao.gmane.org>, Nick Coghlan <public-ncoghlan-Re5JQEeQqe8AvxtiuMwx3w@ciao.gmane.org>
Whoever did that, and whatever they did to cause it, please don't do it again.
I'll manually remove the extra email address's until I (or someone else) can explain why they do that. I hope that's enough for now. I read several python groups though gmane using Thunderbird on Ubuntu, Python-ideas is the only one the email address's are changed like that. (?) Ron