Python Iterators
Every for loop in Python uses an iterator behind the scenes. Knowing how iterators work explains why some objects can be looped over only once, and lets you write your own objects that work with for.
- Iterables vs iterators
- iter(), next() and StopIteration
- Build a custom iterator class
- Useful tools from itertools
Iterable vs iterator
- An iterable is anything you can loop over: a list, string, tuple, dict, set, file or range.
- An iterator is the object that actually hands out the items, one at a time, and remembers its position.
iter() gets an iterator from an iterable, and next() asks it for the next item:
colors = ["red", "green", "blue"]it = iter(colors) print(next(it))print(next(it))print(next(it))red green blue
When there are no items left, next() raises StopIteration:
it = iter([1])print(next(it))print(next(it))1
Traceback (most recent call last):
File "main.py", line 3, in <module>
print(next(it))
~~~~^^^^
StopIterationPass a default to next() to avoid the error:
it = iter([])print(next(it, "no more items"))no more items
How a for loop works
A for loop is a while loop that calls iter() once and next() until StopIteration:
items = ["a", "b", "c"] it = iter(items)while True: try: item = next(it) except StopIteration: break print(item)a b c
Iterators are used up
An iterator moves forward only. Once it is exhausted it stays empty. A list can be looped over many times because each for loop asks it for a fresh iterator.
squares = map(lambda x: x * x, [1, 2, 3]) # map returns an iteratorprint(list(squares))print(list(squares)) # already used up nums = [1, 2, 3]print(sum(nums), sum(nums)) # a list gives a new iterator each time[1, 4, 9] [] 6 6
Write your own iterator
A class becomes an iterator by defining __iter__() (returning itself) and __next__() (returning the next item or raising StopIteration):
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 for n in Countdown(3): print(n)print(list(Countdown(5)))3 2 1 [5, 4, 3, 2, 1]
Writing a class like this takes a dozen lines. A generator function does the same job in three; see the next lesson, Generators.
Infinite iterators and itertools
Iterators can be infinite because they produce values only when asked. The itertools module has many ready-made ones:
import itertools counter = itertools.count(start=100, step=10)print([next(counter) for _ in range(3)]) print(list(itertools.islice(itertools.cycle("AB"), 5)))print(list(itertools.chain([1, 2], (3, 4), "56")))print(list(itertools.combinations("ABC", 2)))print(list(itertools.product([0, 1], repeat=2)))[100, 110, 120]
['A', 'B', 'A', 'B', 'A']
[1, 2, 3, 4, '5', '6']
[('A', 'B'), ('A', 'C'), ('B', 'C')]
[(0, 0), (0, 1), (1, 0), (1, 1)]Built-ins that return iterators
map(), filter(), zip(), enumerate(), reversed() and open() all return iterators. They are lazy: no work happens until you ask for items.
pairs = zip(["a", "b", "c"], [1, 2, 3])print(next(pairs))print(list(pairs)) # continues from where next() left off('a', 1)
[('b', 2), ('c', 3)]Check your understanding
Predict the output
it = iter("abc")next(it)print(list(it), list(it))['b', 'c'] []
The first next() consumes “a”. list(it) collects the rest, and after that the iterator is empty, so the second list is empty.
Quick quiz
1. Which function gets the next item from an iterator?
iter() creates the iterator; next() advances it.
2. What happens when an iterator has no more items?
for loops catch StopIteration for you.
3. Which two methods make a class an iterator?
Together they form the iterator protocol.
Exercise: even number iterator
Write a class Evens(limit) that iterates over the even numbers from 0 up to and including limit.
Evens(10)
[0, 2, 4, 6, 8, 10]
Hint 1
Store the next value in self.n and add 2 each time.
class Evens: def __init__(self, limit): self.limit = limit self.n = 0 def __iter__(self): return self def __next__(self): if self.n > self.limit: raise StopIteration value = self.n self.n += 2 return value print(list(Evens(10)))[0, 2, 4, 6, 8, 10]
__next__ returns the current even number and moves self.n forward by 2. Once it passes the limit it raises StopIteration, which ends the list() call.
Program Practice
20 Python Iterator & Generator Programs
Every program has a step-by-step explanation, colour-coded code and its real output.
Python iterators FAQ
What is the difference between an iterable and an iterator?
An iterable can produce an iterator (lists, strings, dicts). An iterator produces the items one at a time with next() and remembers its position. Every iterator is also iterable.
Why can I loop over a list twice but not over map()?
A list creates a new iterator for each loop. map() is itself an iterator, so once its items are consumed it is empty.
How do I make a class iterable in Python?
Define __iter__() returning an iterator. The easiest way is to make __iter__ a generator method using yield.