The morning rush arrives as one long stream, but the kitchen works in batches of ten. itertools can slice without copying the whole thing. Which tool groups without losing the leftovers?
itertools provides small building blocks for iterator pipelines. They do not create a full list unless you ask for one, so they compose well with generators. Start with count, islice, chain, and groupby, then combine them instead of writing another index loop. You will also accumulate running totals, duplicate an iterator with tee, and build combinations.
Count without a range
count produces an infinite sequence. Pair it with islice to request only a fixed number of values. islice works on any iterable, not just lists, and returns an iterator.
from itertools import count, islice
print(list(islice(count(10), 3)))A bounded slice of infinity
Join iterables with chain
chain reads one iterable after another without building an intermediate list. It is useful when the pieces come from different sources. Wrap the result in list only when you need to print or reuse it.
from itertools import chain
print(list(chain([1, 2], ["a", "b"])))One stream from several parts
Group adjacent values
groupby groups consecutive items with the same key. It does not sort for you, so sort first when equal keys are scattered. The group is an iterator and must be consumed before advancing to the next key.
from itertools import groupby
data = [("a", 1), ("a", 2), ("b", 3)]
for key, group in groupby(data, key=lambda pair: pair[0]):
print(key, list(group))Runs of equal keys
accumulate and pairwise derive streams
accumulate turns values into running totals; pairwise yields adjacent pairs. Both are lazy and express the relationship without an index loop. Use initial= when the accumulator needs a starting value such as zero.
from itertools import accumulate, pairwise
values = [4, 3, 2]
print(list(accumulate(values)))
print(list(pairwise(values)))
print(list(accumulate(values, initial=0)))Running totals and neighboring pairs
tee duplicates an iterator, but buffers
tee returns independent iterators over one source. It is not a deep copy: values pulled ahead by one branch are buffered for the other. Consume both branches, and prefer a list when the data is small enough to store explicitly.
from itertools import tee
values = iter([1, 2, 3])
a, b = tee(values)
print(list(a))
print(list(b))
print(list(a))Two readers, one buffered source
Combinations and products
combinations chooses unordered groups without repetition; product forms every pairing from the inputs. The number of results grows quickly, so keep the inputs small. These tools are useful for test matrices and small search spaces, not for brute-forcing large data.
from itertools import combinations, product
print(list(combinations(["a", "b", "c"], 2)))
print(list(product([1, 2], ["x", "y"])))Small combinatorial building blocks
Key points
- count can represent an infinite arithmetic sequence.
- islice takes a bounded prefix from any iterable.
- chain joins iterables end to end.
- groupby groups adjacent items; sort first when keys are scattered.
- itertools functions return iterators, not lists.
- accumulate and pairwise derive lazy streams; tee buffers; combinatorics grows fast.
Common mistakes
- Trying to index an itertools iterator directly.
- Forgetting to consume a group before advancing groupby.
- Using groupby on unsorted data and expecting global groups.
- Using tee on a huge stream and assuming it stores no data.
Print the first three values from count(10), then join [1, 2] and [3] with chain.
Try it yourself
Change the values, predict the output, then run the code. The Python runtime is downloaded the first time you press Run, so the first run can take a few seconds.
Practice this lesson
12 questions with immediate explanations.