[Python-Dev] Termination of two-arg iter()

Tim Peters tim.one@comcast.net
Sat, 13 Jul 2002 15:24:55 -0400


A post on c.l.py raises an interesting issue, here illustrated in an
all-Python example:

"""
class C:
    def __init__(self):
        self.i = 0
    def get(self):
        self.i += 1
        if self.i <= 5:
            return self.i
        self.i = 1 / 0

x = iter(C().get, 5)
try:
    while 1:
        print x.next()
except StopIteration:
    pass
print x.next()
"""

That prints 1 thru 4, then dies with a ZeroDivisionError.  This is because
two-arg iter works as documented <wink>:

    The iterator created in this case [iter(o, sentinel)] will call o
    with no arguments for each call to its next() method; if the value
    returned is equal to sentinel, StopIteration will be raised, otherwise
    the value will be returned.

The question is whether this is intentional:  for all other iterators Python
supplies, StopIteration is a "sink state":  once an iterator raises
StopIteration, calling its next() method any number of times again will just
continue raising StopIteration.  Python's calliterobject doesn't arrange for
that, though.

PEP 234 doesn't explicitly say what happens if next() is called after
StopIteration has been raised, although it clearly has in mind a model where
iteration eventually "ends".

The use case from which two-arg iter() got generalized was

    iter(file.readline, "")

and in that case file.readline returns "" forever after hitting EOF the
first time.  So in this specific case, StopIteration acts like a sink too,
but for a reason that sheds no light on the question at hand.

The base question:  does the iteration protocol define what happens if an
iterator's next() method is called after the iterator has raised
StopIteration?  Or is that left up to the discretion of the iterator?

If the answer is that it's the iterator's choice, is 2-argument iter()
making the best choice?  The rub here is that 2-arg iter was (IMO)
introduced to help iteration-ignorant callables fit into the iteration
protocol, and *because* they're iteration-ignorant they may do something
foolish if called again after their "sentinel" value is seen.