C
Clark C. Evans
There is an interesting difference between how exceptions are handled
between iterators constructed from a class, and iterators constructed
from a generator. The following are "mostly equivalent":
class iterable_via_class:
def __iter__(self):
self._next = self._one
return self
def next(self):
return self._next()
def _one(self):
self._next = self._two
return "one"
def _two(self):
self._next = self._stop
return "two"
def _stop(self):
raise StopIteration()
def iterable_via_generator():
yield "one"
yield "two"
print "\nclass:"
for x in iterable_via_class():
print x
print "\ngenerator:"
for x in iterable_via_generator():
print x
However, when exceptions are involved, behavior can be different:
class iterable_via_class:
def __iter__(self):
self._next = self._one
return self
def next(self):
return self._next()
def _one(self):
self._next = self._raise
return "one"
def _raise(self):
self._next = self._two
raise Exception()
def _two(self):
self._next = self._stop
return "two"
def _stop(self):
raise StopIteration()
def iterable_via_generator():
yield "one"
raise Exception()
yield "two"
def test(itergen):
it = iter(itergen())
print it.next()
try:
ignore = it.next()
except:
pass
print it.next()
print "\nclass:"
test(iterablex)
print "\ngenerator:"
test(iterable)
between iterators constructed from a class, and iterators constructed
from a generator. The following are "mostly equivalent":
class iterable_via_class:
def __iter__(self):
self._next = self._one
return self
def next(self):
return self._next()
def _one(self):
self._next = self._two
return "one"
def _two(self):
self._next = self._stop
return "two"
def _stop(self):
raise StopIteration()
def iterable_via_generator():
yield "one"
yield "two"
print "\nclass:"
for x in iterable_via_class():
print x
print "\ngenerator:"
for x in iterable_via_generator():
print x
However, when exceptions are involved, behavior can be different:
class iterable_via_class:
def __iter__(self):
self._next = self._one
return self
def next(self):
return self._next()
def _one(self):
self._next = self._raise
return "one"
def _raise(self):
self._next = self._two
raise Exception()
def _two(self):
self._next = self._stop
return "two"
def _stop(self):
raise StopIteration()
def iterable_via_generator():
yield "one"
raise Exception()
yield "two"
def test(itergen):
it = iter(itergen())
print it.next()
try:
ignore = it.next()
except:
pass
print it.next()
print "\nclass:"
test(iterablex)
print "\ngenerator:"
test(iterable)