The Python iterator interface is defined using a method called __next__ that returns the next element of some underlying sequential series that it represents. In response to invoking __next__, an iterator can perform arbitrary computation in order to either retrieve or compute the next element. Calls to __next__ make a mutating change to the iterator: they advance the position of the iterator. Hence, multiple calls to __next__ will return sequential elements of an underlying series. Python signals that the end of an underlying series has been reached by raising a StopIteration exception during a call to __next__.

The LetterIter class below iterates over an underlying series of letters from some start letter up to but not including some end letter. The instance attribute next_letter stores the next letter to be returned. The __next__ method returns this letter and uses it to compute a new next_letter.

>>> class LetterIter:
            """An iterator over letters of the alphabet in ASCII order."""
            def __init__(self, start='a', end='e'):
                self.next_letter = start
                self.end = end
            def __next__(self):
                if self.next_letter == self.end:
                    raise StopIteration
                letter = self.next_letter
                self.next_letter = chr(ord(letter)+1)
                return letter
    

Using this class, we can access letters in sequence using either the __next__ method or the built-in next function, which invokes __next__ on its argument.

>>> letter_iter = LetterIter()
    >>> letter_iter.__next__()
    'a'
    >>> letter_iter.__next__()
    'b'
    >>> next(letter_iter)
    'c'
    >>> letter_iter.__next__()
    'd'
    >>> letter_iter.__next__()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 12, in next
    StopIteration
    

Iterators are mutable: they track the position in some underlying sequence of values as they progress. When the end is reached, the iterator is used up. A LetterIter instance can only be iterated through once. After its __next__() method raises a StopIteration exception, it continues to do so from then on. Typically, an iterator is not reset; instead a new instance is created to start a new iteration.

Iterators also allow us to represent infinite series by implementing a __next__ method that never raises a StopIteration exception. For example, the Positives class below iterates over the infinite series of positive integers. The built-in next function in Python invokes the __next__ method on its argument.

>>> class Positives:
            def __init__(self):
                self.next_positive = 1;
            def __next__(self):
                result = self.next_positive
                self.next_positive += 1
                return result
    >>> p = Positives()
    >>> next(p)
    1
    >>> next(p)
    2
    >>> next(p)
    3