Iterators: iter, next, iter(), next()
Every time you write for x in something:, Python is using the iterator protocol under the hood. Understanding it lets you create objects that work seamlessly with for loops, comprehensions, zip(), map(), and every other iteration context in Python.
Iterable vs Iterator
These two terms are related but distinct:
- An iterable is any object you can loop over. It has
__iter__()that returns an iterator. - An iterator is the object that produces values one at a time. It has both
__iter__()(returnsself) and__next__()(returns the next value, or raisesStopIterationwhen exhausted).
iter() and next() Built-ins
iter(obj) calls obj.__iter__() and returns the iterator. next(it) calls it.__next__().
How for Loops Use the Protocol
The for loop is syntactic sugar for the iterator protocol:
Python calls iter() on the object once, then calls next() repeatedly until StopIteration is raised. The exception is caught automatically โ you never see it.
Building a Custom Iterator
To make an iterator, implement both __iter__ and __next__:
Iterables That Create Fresh Iterators
Notice that Countdown is both an iterable and an iterator โ calling iter() on it returns itself. This means you can only iterate it once. Lists are different: each call to iter(list) creates a fresh, independent iterator:
To make a reusable iterable that creates fresh iterators, separate the iterable class from the iterator class:
Infinite Iterators
An iterator doesn't have to end โ it can yield values forever. Useful for sequences like natural numbers or repeating patterns:
itertools.islice to Limit Infinite Iterators
itertools.islice(it, n) lets you take just the first n items from any iterator โ essential for working with infinite ones:
Practical Example: File Iterator
A classic real-world iterator is reading a large file line by line โ you don't load the entire file into memory:
Quick Reference
| Concept | Method/Function | Purpose |
|---|---|---|
| Make iterable | __iter__(self) | Return an iterator |
| Make iterator | __next__(self) | Return next value or raise StopIteration |
| Get iterator | iter(obj) | Call obj.__iter__() |
| Get next value | next(it) | Call it.__next__() |
| Limit iterator | itertools.islice(it, n) | Take first n items |
| Check exhausted | next(it, default) | Return default instead of raising |
Knowledge Check
What is the difference between an iterable and an iterator?
What does an iterator's `__iter__` method return?
What happens when you call `next()` on an exhausted iterator?