Search the courses, subjects, tracks, games and features, or jump straight to a page.
Learn it your way
A short class with Sarah and her students. The other ways are under More.
Sign up free to listen. Transcripts are free to read.
We've learned about the iterator protocol. Now let's see how to create a custom iterator. Imagine we want a countdown sequence. How can we do this?
We can use a class with __iter__() and __next__() methods!
Exactly! Let's build one that counts down from a number to 1.
Couldn't we just use range(3, 0, -1)?
For a plain countdown, yes. But a class lets us add our own rules, like skipping numbers or logging each tick, and the same pattern works for sequences range can't describe.
First, initialize your starting number in __init__(). Then, __iter__() will return self since the object is its own iterator. Finally, __next__() will return the current number and decrease it.
What happens when the number reaches zero?
We raise StopIteration to stop iteration.
Won't that exception crash the for loop?
No. The for loop catches StopIteration itself and simply ends. You only see the exception if you call next() by hand on an iterator that has run out.
Here's a puzzle. If we loop over the same CountDown object twice, what does the second loop print?
3, 2, 1 again?
Try it: nothing at all. After the first loop, current is 0, so __next__() raises StopIteration straight away.
So an iterator is used up after one pass?
Yes. To count down again, create a new CountDown, or write an iterable class whose __iter__() returns a fresh iterator each time.
A user-defined class that implements the iterator protocol.
Variables inside the iterator that track progress.
Signals that the iterator has no more data to yield.
Calls iter() once, then next() repeatedly until StopIteration is raised.
An iterator that returns self from __iter__() is used up after one loop.
What a for loop does behind the scenes, done by hand with iter() and next():
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
cd = CountDown(3)
it = iter(cd)
print(it is cd) # the object is its own iterator
print(next(it))
print(next(it))
print(next(it))
try:
next(it)
except StopIteration:
print("StopIteration: the countdown is over")A solution to the practice exercise, an EvenNumbers iterator that counts in twos up to a limit:
class EvenNumbers:
def __init__(self, limit):
self.limit = limit
self.current = 2
def __iter__(self):
return self
def __next__(self):
if self.current > self.limit:
raise StopIteration
value = self.current
self.current += 2
return value
for n in EvenNumbers(10):
print(n)
print(list(EvenNumbers(7)))An iterator is used up after one pass, while an iterable class that returns a fresh iterator from __iter__() can be looped over again and again:
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
class CountDownRange:
"""An iterable: every loop gets a brand-new CountDown iterator."""
def __init__(self, start):
self.start = start
def __iter__(self):
return CountDown(self.start)
cd = CountDown(2)
print("iterator, first pass:", list(cd))
print("iterator, second pass:", list(cd))
r = CountDownRange(2)
print("iterable, first pass:", list(r))
print("iterable, second pass:", list(r))Iter hands back self, next hands out one; raise StopIteration when the counting is done.
A lighthouse keeper counts down the seconds before the lamp flashes: he says the number, takes one off, and when he reaches zero he stops calling. His notebook holding the current number is the iterator's internal state.
"Self, step, stop": __iter__() returns self, __next__() steps forward one value, and StopIteration stops the loop.
I.N.S. - Iter returns self, Next returns a value, Stop with StopIteration.
Term
Which two methods make a class an iterator?
Click to flip
Term
What should `__iter__()` return in a custom iterator?
Click to flip
Term
How does an iterator say it has no more values?
Click to flip
Term
What does a `for` loop call behind the scenes?
Click to flip
Term
What happens if you loop over an exhausted iterator again?
Click to flip
Rules an iterator follows (__iter__() and __next__()).
Exception to indicate iteration completion.
The position or data the iterator keeps track of during iteration.
A class you write that implements the iterator protocol to produce its own sequence of values.
An object whose __iter__() returns an iterator, such as a list, a string or a range.
An iterator that has already raised StopIteration; asking it for more values raises StopIteration again.
A custom iterator is a user-defined object that follows the iterator protocol, managing its own internal state and yielding values one-by-one when iterated over.
By implementing the methods __iter__() and __next__(), you can create custom iterators in Python. These iterators keep track of their internal position and define precisely what happens on each iteration, including when the iteration ends by raising StopIteration.
Creating a custom iterator allows you to define your own iteration behavior by implementing the iterator protocol—__iter__() and __next__() methods—in a class. This enables you to control how your object produces a sequence of values during iteration.
While Python provides many built-in iterators, there are situations where you need specialized iteration logic. A custom iterator is a class that explicitly implements the iterator protocol:
__iter__(self): Returns the iterator object itself. This allows the object to be used in iteration contexts such as loops.__next__(self): Returns the next value in the sequence. When no more values are available, it raises StopIteration to signal the end.class CountDown:
def __init__(self, start):
self.current = start
def __iter__(self):
# Return the iterator object itself
return self
def __next__(self):
if self.current <= 0:
# End iteration when countdown finishes
raise StopIteration
else:
# Return current value and decrement
value = self.current
self.current -= 1
return value
# Using the custom iterator
cd = CountDown(3)
for number in cd:
print(number)Output:
3
2
1
__init__(self, start): Initializes the countdown start value.__iter__(self): Returns the iterator object (self) so it can be used in iteration contexts.__next__(self): Returns the current value and decrements it. Raises StopIteration when the countdown is over.for loop internally calls these methods to retrieve values until the sequence ends.Notice the order inside __next__(): it saves the current value, decrements the counter, then returns the saved value. That is why the first number printed is 3, not 2. Because CountDown returns self from __iter__(), the object is its own iterator, so it can be looped over only once: a second for loop over the same cd prints nothing, because current is already 0.
Key Points: three things to remember about custom iterators.
for loop internally calls iter() and then repeatedly calls next() on the iterator.StopIteration is necessary to signal the end of the iteration to the loop or caller.Think of a countdown timer at a race start. Each tick reduces the timer by one. Once the timer hits zero, it stops—just like the StopIteration ends the iteration.
Implement a custom iterator class EvenNumbers that iterates over even numbers from 2 up to a given limit. One possible solution is in the Examples section, and the Audio Book Style Summary in the Audio Book section retells this lesson in short spoken chunks.