The Iterator Protocol
Make objects iterable by implementing __iter__ and __next__
Overview
The iterator protocol is the contract that powers every for loop, comprehension, and in check. An iterable implements __iter__, returning an iterator; an iterator implements __next__, returning successive values and raising StopIteration when exhausted. Understanding this protocol lets you build lazy, memory-efficient sequences and integrate custom types with all of Python's iteration machinery.
Syntax / Usage
Separate the iterable from its iterator so multiple independent iterations work correctly.
class Countdown:
def __init__(self, start: int) -> None:
self.start = start
def __iter__(self) -> "CountdownIterator":
return CountdownIterator(self.start)
class CountdownIterator:
def __init__(self, current: int) -> None:
self.current = current
def __iter__(self) -> "CountdownIterator":
return self
def __next__(self) -> int:
if self.current <= 0:
raise StopIteration
self.current -= 1
return self.current + 1
for n in Countdown(3):
print(n) # 3, 2, 1
iter(obj) calls __iter__ and next(it) calls __next__; for loops do both automatically.
Examples
A lazy, potentially infinite iterator that never materializes a list:
class Fibonacci:
def __iter__(self):
a, b = 0, 1
while True:
yield a # a generator method satisfies the protocol
a, b = b, a + b
fib = iter(Fibonacci())
print([next(fib) for _ in range(7)]) # [0, 1, 1, 2, 3, 5, 8]
Any iterator is also an iterable, so it plugs into standard tools:
import itertools
def evens():
n = 0
while True:
yield n
n += 2
first_five = list(itertools.islice(evens(), 5))
print(first_five) # [0, 2, 4, 6, 8]
Common Mistakes
- Returning
selffrom__iter__on a stateful object, so a second loop resumes mid-sequence - Forgetting to raise
StopIteration, causing an infinite loop - Confusing iterables (re-iterable) with iterators (single-use, exhaust once)
- Reusing an exhausted iterator and getting an empty result with no error
- Reimplementing
__next__by hand when a generator (yield) would be far simpler
See Also
python-generators python-dataclasses python-context-managers