generators and exceptions
Clark C. Evans
cce at clarkevans.com
Mon Mar 17 08:27:55 CET 2003
Thank you for responding Tim, as you expected my question is less
about what generator behavior is, as it is about what generator
behavior with regard to exceptions could be.
On Sat, Mar 15, 2003 at 11:20:46PM -0500, Tim Peters wrote:
| > from __future__ import generators
| > class MyExc(Exception): pass
| > def mygen(val):
| > while val > 0:
| > if val % 2: raise MyExc
| > yield val
val -= 1
| Read the PEP and your expectations will change <wink>. The code exactly as
| you gave should obviously produce only 4 (val is set to -1 and so the loop
| exits). With the suggested guess at what was intended, it should still
| produce only 4, but due to raising an exception within the generator when
| val is 3 (instead of falling off the end because val is -1).
Right. I did, but this still didn't stop my expectations. I was wishing
that I could resume the generator after the exception was thrown.
| > I expect 4, 2 beacuse an equivalent iterator would produce 4, 2.
| Unclear what that means ("equivalent iterator") -- if you raise an exception
| within *any* function, and don't catch it within that function, the
| function's useful life ends. Generator functions aren't an exception to
| this, and neither are functions implementing any other kind of iterator.
To me, generators as a great syntax for writing complicated
iterators, the 'equivalent iterator' would be something like...
def __iter__(self): return self
def __init__(self, val):
self.val = val
val = self.val
if val < 1: raise StopIteration
self.val = val - 1
if val % 2: raise MyExc
What's interesting here is that I can raise an exception from
within the iterator and it doesn't 'dismiss' the iterator; that
is, I can call next() again and it may produce a second time.
| > It seems that generators die on the first exception... is there a way
| > around this?
| > I'm asking beacuse I'm using generators in a non-blocking database
| > system, where I want to raise WouldBlock in my generator, but still be
| > able to call the generator at a later time when it may not block...
| Resuming a generator that has raised a (any) exception results in
| StopIteration getting raised immediately, so, no, you can't do that
Is there a serious technical reason for this behavior? Could it
have worked some other way, i.e., why not let a generator survive
an exception, as the above iterator does so well. A generator is
fundamentally different from a function; I tend to picture it as
great syntax sugar for making iterators. However, and unfortunately,
a great many of my iterators raise "recoverable exceptions".
More information about the Python-list