[Python-Dev] Return from generators in Python 3.2
Ron Adam
rrr at ronadam.com
Fri Aug 27 07:42:22 CEST 2010
On 08/26/2010 07:25 PM, Guido van Rossum wrote:
> That's not my experience. I wrote a trampoline myself (not released
> yet), and found that I had to write a lot more code to deal with the
> absence of yield-from than to deal with returns. In my framework,
> users write 'raise Return(value)' where Return is a subclass of
> StopIteration. The trampoline code that must be written to deal with
> StopIteration can be extended trivially to deal with this. The only
> reason I chose to use a subclass is so that I can diagnose when the
> return value is not used, but I could have chosen to ignore this or
> just diagnose whenever the argument to StopIteration is not None.
I'm currently playing around with a trampoline version based on the example
in PEP 342. Some of the things I found are ...
* In my version I seperated the trampoline from the scheduler. Having it
as a seperate class made the code cleaner and easier to read. A trampoline
instance can be ran without the scheduler. (An example of this is below.)
The separate Scheduler only needs a few methods in a coroutine wrapper to
run it. It really doesn't matter what's inside it as long as it
has a resume method that the scheduler can understand, and it returns in a
timely manner so it doesn't starve other coroutines.
* I've found that having a Coroutine class, that has the generator methods
on it, is very useful for writing more complex coroutines and generators.
* In a true trampoline, all sub coroutines are yielded out to the
trampoline resume loop before their send method is called, so "yield from"
isn't needed with a well behaved Trampoline runner.
I think "yield from"'s value is that it emulates a trampolines performance
without needing a stack to keep track of caller coroutines. It also saves
a lot of looping if you want to write coroutines with sub coroutines
without a trampoline runner to run them on.
* Raising StopIteration(value) worked just fine for setting the last value.
Getting the value from the exception just before returning it is still a
bit clumsy... I currently use.
return exc.args[0] if exc.args else None
Maybe I've overlooked something?
My version of the Trampoline class handles the return value since it
already has it handy when it gets a StopIteration exception, so the user
doesn't need to this, they just need to yield the last value out the same
as they do anywhere else.
I wonder if "yield from" may run into pythons stack limit? For example...
"""
Factorial Function.
"""
def f(n, k=1):
if n != 0:
return f(n-1, k*n)
else:
return k
def factoral(n):
return f(n)
if __name__ == "__main__":
print(factoral(1000))
This aborts with:
RuntimeError: maximum recursion depth exceeded in comparison
This one works just fine.
"""
Factorial Trampoline.
"""
from coroutine.scheduler import Trampoline
def tramp(func):
def wrap(*args, **kwds):
t = Trampoline(func(*args, **kwds))
return t.run()
return wrap
def f(n, k=1):
if n != 0:
yield f(n-1, k*n)
else:
yield k
@tramp
def factoral(n):
yield f(n)
if __name__ == "__main__":
print(factoral(10000)) # <---- extra zero too!
But if I add another zero, it begins to slow to a crawl as it uses swap
space. ;-)
How would a "yield from" version compare?
I'm basically learning this stuff by trying to break this thing, and then
trying to fix what breaks as I go. That seems to be working. ;-)
Cheers,
Ron Adam
More information about the Python-Dev
mailing list