Consider the following code, which creates a generator that immediately returns 1, and then catches the StopIteration twice.
def generatorfunction(): if False: yield return 1
def get_return(gen): try: next(gen) except StopIteration as e: return e.value else: raise ValueError("Generator was not ready to stop.")
gen = generatorfunction() get_return(gen) #=> 1 get_return(gen) #=> None
The first time StopIteration is raised, it contains the returned value. If StopIteration is forced again, the value is missing.
What about keeping the return value for subsequent raises? Perhaps as an attribute on the generator object? The main disadvantage is that it'd add another reference, keeping the return value alive as long as the generator is alive. However, I don't think you'll want to keep a dead generator around anyway.
Background: I made a trampoline for a toy problem, using generators as coroutines to recurse.
When it came time to memoize it, I had to couple the memoization with the trampoline, because I could not cache the answer before it was computed, and I could not cache the generator object because it would not remember its return value later.
I would have attached the returned value to the generator object, but for some reason, generator and coroutine objects can't take attributes. Maybe I should ask for that feature instead.
Either feature would allow the concept of a coroutine that is also a thunk.