What It Is
The Iterator pattern walks through a collection without exposing how it is stored. The caller asks "is there more?" and "give me the next one" — whether the data lives in a list, a tree or a database stays private.
For plain lists and dicts, Python's for loop already is your iterator. The pattern matters when the storage is hidden or the walk is complicated — and Python's answer is __iter__: implement it, often as a generator, and any object works with for ... in.
Example: Walking a Playlist
The playlist keeps its songs private. It implements __iter__ as a generator that yields one song at a time — the storage behind it could change completely without breaking any caller.
A generator does the walking
The loop never touches the list inside the playlist. Swap the list for a linked list or lazy loading, and the same for loop still runs unchanged.
Output: complete Python program
from typing import Iterator
class Playlist:
def __init__(self, songs: list[str]) -> None:
self._songs = songs
# The iterator walks the collection without exposing the list.
def __iter__(self) -> Iterator[str]:
for song in self._songs:
yield song
playlist = Playlist(["intro", "verse", "chorus"])
for song in playlist:
print("playing:", song)
When to Use It
Use it when the collection's insides must stay private, when walking is nontrivial (trees, pages from an API), or when you need several independent walks at once.
Skip it for lists and dicts in your own code — looping over them directly is shorter, faster and known to every reader.
Pros and Cons
Pros: storage stays private and changeable; several walks can run independently — each for loop gets a fresh generator; complicated traversal is written once.
Cons: more code than a direct loop; a live iterator over a changing collection is a classic source of subtle bugs.