iterators: class vs generator

Discussion in 'Python' started by Clark C. Evans, Aug 24, 2004.

  1. 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)
     
    Clark C. Evans, Aug 24, 2004
    #1
    1. Advertising

  2. "Clark C. Evans" <> writes:

    > 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":


    [...]

    > However, when exceptions are involved, behavior can be different:


    (It would have been nice if you'd explained what the differences were
    in your post, and given that you'd didn't if you'd made sure the
    examples were correct -- got a NameError for iterable...)

    Here's a better illustration:

    >>> def iterable_via_generator():

    .... yield "one"
    .... raise Exception()
    .... yield "two"
    ....
    >>> i = iterable_via_generator()
    >>> i.next()

    'one'
    >>> i.next()

    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    File "<stdin>", line 3, in iterable_via_generator
    Exception
    >>> i.next()

    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    StopIteration

    I agree this is surprising. I am vaguely aware of some effort being
    put in to make generators "stick", i.e. this doesn't surprise me so
    much:

    >>> def g():

    .... yield 1
    .... return
    .... yield 2
    ....
    >>> i = g()
    >>> i.next()

    1
    >>> i.next()

    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    StopIteration
    >>> i.next()

    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    StopIteration

    I'd guess the behaviour you've noticed is an unintended consequence of
    this. I'd also be willing to be persuaded that it's a bug, although
    others might disagree, dunno. File an issue, anyway?

    Cheers,
    mwh

    --
    I would hereby duly point you at the website for the current pedal
    powered submarine world underwater speed record, except I've lost
    the URL. -- Callas, cam.misc
     
    Michael Hudson, Aug 25, 2004
    #2
    1. Advertising

  3. On Wed, 25 Aug 2004 11:24:41 GMT, Michael Hudson <> wrote:
    >
    > Here's a better illustration:
    >
    > >>> def iterable_via_generator():

    > .... yield "one"
    > .... raise Exception()
    > .... yield "two"
    > ....
    > >>> i = iterable_via_generator()
    > >>> i.next()

    > 'one'
    > >>> i.next()

    > Traceback (most recent call last):
    > File "<stdin>", line 1, in ?
    > File "<stdin>", line 3, in iterable_via_generator
    > Exception
    > >>> i.next()

    > Traceback (most recent call last):
    > File "<stdin>", line 1, in ?
    > StopIteration



    This is correct behaviour according to PEP 255:
    """
    If an unhandled exception-- including, but not limited to,
    StopIteration --is raised by, or passes through, a generator function,
    then the exception is passed on to the caller in the usual way, and
    subsequent attempts to resume the generator function raise
    StopIteration. In other words, an unhandled exception terminates a
    generator's useful life.
    """
     
    Andrew Durdin, Aug 25, 2004
    #3
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Martin Maurer
    Replies:
    3
    Views:
    4,864
    Peter
    Apr 19, 2006
  2. Marcin Kaliciñski

    Iterators and reverse iterators

    Marcin Kaliciñski, May 8, 2005, in forum: C++
    Replies:
    1
    Views:
    488
    Kai-Uwe Bux
    May 8, 2005
  3. TheDustbustr
    Replies:
    1
    Views:
    454
    Sami Hangaslammi
    Jul 25, 2003
  4. Bernhard Mulder

    cloning generator iterators

    Bernhard Mulder, Aug 20, 2006, in forum: Python
    Replies:
    2
    Views:
    309
    Michael
    Aug 20, 2006
  5. , India
    Replies:
    10
    Views:
    1,081
    James Kanze
    Aug 8, 2009
Loading...

Share This Page