![](https://secure.gravatar.com/avatar/f3ba3ecffd20251d73749afbfa636786.jpg?s=120&d=mm&r=g)
Jacob Holm wrote:
Then let me revisit my earlier statement that when close() catches a StopIteration with a non-None value, it should either return it or raise an exception. Since the value is not saved, a second close() will neither be able to return it, nor raise a StopIteration with it. Therefore I now think that raising a RuntimeError in that case is the only right thing to do.
Remember, close() is designed to be about finalization. So long as the generator indicates that it has finished (i.e. by reraising GeneratorExit or raising StopIteration with or without a value), the method has done its job. Raising a RuntimeError for a successfully closed generator doesn't make any sense. So if someone wants the return value, they'll need to either use next(), send() or throw() and catch the StopIteration themselves, or else use 'yield from'. That said, creating your own stateful wrapper that preserves the last yield value and the final return value of a generator iterator is also perfectly possible: class CaptureGen(object): """Capture and preserve the last yielded value and the final return value of a generator iterator instance""" NOT_SET = object() def __init__(self, geniter): self.geniter = geniter self._last_yield = self.NOT_SET self._return_value = self.NOT_SET @property def last_yield(self): if self._last_yield is self.NOT_SET: raise RuntimeError("Generator has not yielded") return self._last_yield @property def return_value(self): if self._return_value is self.NOT_SET: raise RuntimeError("Generator has not returned") return self._return_value def _delegate(self, meth, *args): try: val = meth(*args) except StopIteration, ex: if self._return_value is self.NOT_SET: self._return_value = ex.value raise raise StopIteration(self._return_value) self._last_yield = val return val def __next__(self): return self._delegate(self.geniter.next) next = __next__ def send(self, val): return self._delegate(self.geniter.send, val) def throw(self, et, ev=None, tb=None): return self._delegate(self.geniter.throw, et, ev, tb) def close(self): self.geniter.close() return self._return_value Something like that may actually turn out to be useful as the basis for an enhanced coroutine decorator, similar to the way one uses contextlib.contextmanager to turn a generator object into a context manager. The PEP is quite usable for refactoring without it though.
It doesn't matter if there is only one use case, as long as it is a common one. And we already have that: Greg Ewing's "refactoring".
I remain unconvinced that the "initial next()" issue isn't also a problem for that use case, but I am not going to argue about this.
For refactoring, the pattern of passing in a "start" value for use in the first yield expression in the subiterator should be adequate. That's enough to avoid injecting spurious "None" values into the yield sequence. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia ---------------------------------------------------------------