🎓 A1Learner
🔍

Python The Iterator Protocol

The iterator protocol is the agreement that lets a for loop walk an object.

Iterable and iterator

A list is an iterable. The call iter() asks it for an iterator.

numbers = [10, 20, 30]
walker = iter(numbers)
print(next(walker))
print(next(walker))

Two methods do the work

A custom iterator needs two methods. This first version has no stop check yet.

class Countdown:
    def __init__(self, start):
        self.current = start

    def __iter__(self):
        return self

    def __next__(self):
        value = self.current
        self.current -= 1
        return value

ticks = Countdown(3)
print(next(ticks))
print(next(ticks))

StopIteration ends the walk

Add a stop check to __next__. Raise StopIteration when no items remain.

class Countdown:
    def __init__(self, start):
        self.current = start

    def __iter__(self):
        return self

    def __next__(self):
        if self.current <= 0:
            raise StopIteration
        value = self.current
        self.current -= 1
        return value

ticks = Countdown(2)
print(next(ticks))
print(next(ticks))
try:
    next(ticks)
except StopIteration:
    print("Done")

A for loop uses the same steps

A for loop calls iter() first. It then calls next() until StopIteration appears.

class Countdown:
    def __init__(self, start):
        self.current = start

    def __iter__(self):
        return self

    def __next__(self):
        if self.current <= 0:
            raise StopIteration
        value = self.current
        self.current -= 1
        return value

for number in Countdown(3):
    print(number)

Example

class Countdown:
    def __init__(self, start):
        self.current = start

    def __iter__(self):
        return self

    def __next__(self):
        if self.current <= 0:
            raise StopIteration
        value = self.current
        self.current -= 1
        return value

print("With for:")
for number in Countdown(3):
    print(number)

steps = Countdown(2)
print("With next:")
print(next(steps))
print(next(steps))

print("With list:")
print(list(Countdown(3)))

Key points

  • Iterable: an object you can loop over, like a list or a string
  • Iterator: an object that hands out items one at a time
  • Use __iter__() to return the iterator for an object
  • Use __next__() to take the next item from an iterator
  • StopIteration: the signal that no items are left

In this example, Countdown stores its next value in current. The __iter__ method returns self, so the object is its own iterator. The __next__ method hands out current and then lowers it by one. It raises StopIteration when current reaches zero. The for loop walks a fresh Countdown from 3 to 1. The next() calls walk another Countdown by hand. The list() call walks a third Countdown and collects every value.

Note

An iterator returns itself from __iter__().

Asking again does not restart an iterator that is already spent.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Python Playground

Exercise

What must __next__ do when an iterator has no items left?