Write a custom Python class that follows the iterator protocol.
An object is an iterator if it implements two methods: __iter__ returns the iterator itself, and __next__ returns the next value or raises StopIteration when there are none.
class Countdown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
self.current -= 1
return self.current
for value in Countdown(3):
print(value) # 2, 1, 0
it = Countdown(2)
print(next(it)) # 1
print(next(it)) # 0The for loop calls iter on the object, which gives back the iterator, and then calls next on it repeatedly until StopIteration is raised. Because the class returns itself from __iter__, the object is both iterable and an iterator — which is the simplest design, and the one used by generators themselves.
Two details to explain. The StopIteration exception is not an error, it is the termination signal, and it must be raised by __next__ when the sequence is finished — raising anything else propagates a genuine error. And the state lives on the instance, so each new Countdown object has its own independent countdown.
An alternative design splits the two roles: the container implements __iter__ and returns a separate iterator object that implements __next__. That is what a list does — iter on a list gives you a list_iterator, and the list itself is not an iterator. You only need that split when one object should support several independent traversals, which is the case for containers but rarely for your own classes.