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?