Iterator
Walk the items of any collection one at a time, without knowing whether it's a list, a tree, or pages from an API.
Big idea
An Iterator gives one uniform way to visit a collection's elements in turn — "give me the next one" — while hiding how the collection is stored. The loop that consumes items doesn't care whether they come from an array, a tree walk, or a paginated web API fetched lazily.
The problem it solves
You have orders in three places: a Python list, a binary search tree keyed by date, and a REST API that returns 50 at a time. Code that totals them would otherwise need three loops: index arithmetic, a recursive walk, and a while next_page: loop with request logic. Each consumer re-implements the traversal, and changing the storage breaks every consumer.
Think of it like
A TV remote's "next channel" button. You don't need to know whether channels come over cable, satellite, or the internet, or how they're numbered internally — you press next, you get the next one. Different TVs (collections) can even offer different orders: by number, favourites only, recently watched.
Structure
Python builds this into the language: anything with __iter__ works in a for loop, and generator functions (yield) write the iterator for you.
In code
class Node:
def __init__(self, key, left=None, right=None):
self.key, self.left, self.right = key, left, right
class OrderTree:
def __init__(self, root):
self.root = root
def __iter__(self): # in-order traversal, lazily
stack, node = [], self.root
while stack or node:
while node:
stack.append(node); node = node.left
node = stack.pop()
yield node.key
node = node.right
class OrderApi: # pretend each page is a network call
PAGES = {None: (["o-101", "o-102"], "p2"), "p2": (["o-103"], "p3"), "p3": (["o-104"], None)}
def __init__(self):
self.requests = 0
def __iter__(self):
token = None
while True:
self.requests += 1
items, token = self.PAGES[token]
yield from items # pages are fetched only as needed
if token is None:
return
tree = OrderTree(Node(20, Node(10, Node(5)), Node(30, None, Node(40))))
api = OrderApi()
def show(name, orders): # one consumer for every collection
print(name, list(orders))
show("tree:", tree)
show("api: ", api)
print("api requests:", api.requests)
lazy = OrderApi()
first = next(iter(lazy))
print("first order:", first, "| requests so far:", lazy.requests)output
tree: [5, 10, 20, 30, 40]
api: ['o-101', 'o-102', 'o-103', 'o-104']
api requests: 3
first order: o-101 | requests so far: 1Run and edit it in the app.
Asking for one order from the API made exactly one request — iterators are lazy, producing items only when asked.
def countdown(n):
while n > 0:
yield n
n -= 1
numbers = countdown(3)
print(list(numbers))
print(list(numbers))What does it print? Show the answer
[3, 2, 1]
[]Run and edit it in the app.
A generator object is a single-use iterator: once it's exhausted, it stays exhausted. Collections like OrderTree hand out a fresh iterator on every for, which is why the iterable/iterator split matters.
What does the Iterator pattern hide from the code that consumes items?
Watch it
When to use it — and when not to
Use it whenever a collection's internal structure is complex or might change, when you need several traversal orders (in-order, breadth-first, filtered), or when items are expensive or infinite and should be produced lazily (pages, file lines, streams). For a plain list, the language's built-in iteration is already the pattern — don't write a class.
Pros and cons
| Pros | Cons |
|---|---|
| One loop style for every collection | Single-pass iterators surprise people when reused |
| Lazy: handles huge or infinite sequences | Modifying a collection while iterating is error-prone |
| Several traversals of the same data | Overkill for simple arrays in languages with built-in loops |
How it relates
- Iterators walk Composite trees without exposing their structure.
- Visitor performs operations on elements; Iterator just delivers them — they're often combined.
- Factory Method is how a collection creates the right iterator (
__iter__is one). - Memento can capture an iterator's position to resume later.
Why is a generator a good iterator for a paginated API?
Key terms
- Iterator — An object that returns a collection's elements one at a time.
- Iterable — A collection that can produce a fresh iterator over itself.
- Generator — A Python function using yield that produces an iterator automatically.
- Lazy evaluation — Producing each item only when it's requested.