🐍 Python Course · Stage 3 · Lesson 35 of 89 · itertools — Iterators
Course Overview →

🔵 Read Beginner and Intermediate tabs. The Expert tab has the internals — worth reading once you finish Stage 3.

← collections — Containers sys — System Params →
thecodex.expert · The Codex Family of Knowledge
Python stdlib

Python itertools — Iterator Building Blocks module

chain, product, combinations, groupby, accumulate — the iterator toolkit that makes sequence operations both elegant and memory-efficient.

Python 3.13 Standard Library Last verified:
Canonical Definition

The Python itertools module provides a collection of fast, memory-efficient iterator building blocks — functions for combining, filtering, grouping, and generating sequences lazily, following the iterator algebra model.

🟩 Beginner

Combining and transforming sequences

What you'll learn: chain() for flattening iterables, islice() for memory-efficient slicing, product() for Cartesian products.

How to read this tab: Try itertools.chain([1,2], [3,4], [5,6]) in the REPL. Then try itertools.product("AB", range(3)). These are the most immediately useful itertools functions.

⏱ 25 min 📄 2 sections 🔶 Prerequisite: Comprehensions
One sentence

itertools is a toolkit of building blocks for working with sequences — it gives you efficient, memory-friendly tools for chaining, grouping, filtering, combining, and repeating iterables.

chain.from_iterable() is more memory-efficient than chain(). itertools.chain([1,2],[3,4]) takes multiple arguments. itertools.chain.from_iterable([[1,2],[3,4]]) takes one iterable of iterables — useful when you're generating the sublists lazily.

Combining iterables

Pythonitertools_combining.py
import itertools

# chain — flatten multiple iterables into one
a = [1, 2, 3]
b = [4, 5, 6]
c = [7, 8, 9]
combined = list(itertools.chain(a, b, c))
print(combined)   # [1, 2, 3, 4, 5, 6, 7, 8, 9]

# chain.from_iterable — flatten one level
nested = [[1, 2], [3, 4], [5, 6]]
flat = list(itertools.chain.from_iterable(nested))
print(flat)   # [1, 2, 3, 4, 5, 6]

# zip_longest — zip, filling missing values with fillvalue
names  = ["Priya", "Rahul"]
scores = [95, 88, 72]   # longer
for n, s in itertools.zip_longest(names, scores, fillvalue="N/A"):
    print(f"{n}: {s}")
# Priya: 95 | Rahul: 88 | N/A: 72
💡

The mathematical difference: combinations(["A","B","C"], 2) gives AB, AC, BC (order doesn't matter, no repeats). permutations(["A","B","C"], 2) gives AB, AC, BA, BC, CA, CB (order matters). combinations_with_replacement allows repeats (AA, AB, AC, BB, BC, CC).

Permutations and combinations

Pythonitertools_combinatorics.py
import itertools

items = ["A", "B", "C"]

# product — cartesian product (nested for loops)
for p in itertools.product([0, 1], repeat=3):
    print(p)   # all 8 binary combinations of length 3

# permutations — all orderings
for p in itertools.permutations(items, 2):
    print(p)   # ('A','B'), ('A','C'), ('B','A'), ...  — 6 total

# combinations — unique subsets (order doesn't matter)
for c in itertools.combinations(items, 2):
    print(c)   # ('A','B'), ('A','C'), ('B','C') — 3 total

# combinations_with_replacement — can repeat elements
for c in itertools.combinations_with_replacement("ABC", 2):
    print(c)   # ('A','A'), ('A','B'), ('A','C'), ('B','B'), ...

Infinite iterators never stop. itertools.count(1) counts 1, 2, 3, ... forever. itertools.cycle([A,B,C]) repeats A, B, C, A, B, C, ... forever. Always use them with islice() or takewhile() to stop iteration: list(islice(count(1), 10)) gives [1..10].

Infinite iterators and slicing

Pythonitertools_infinite.py
import itertools

# count — count from start by step
for n in itertools.count(10, 2):     # 10, 12, 14, ...
    if n > 20: break
    print(n)

# cycle — repeat a sequence forever
colours = itertools.cycle(["red", "green", "blue"])
print([next(colours) for _ in range(7)])
# ['red', 'green', 'blue', 'red', 'green', 'blue', 'red']

# repeat — repeat a value (n times, or forever)
ones = list(itertools.repeat(1, 5))    # [1, 1, 1, 1, 1]

# islice — slice any iterator (not just sequences)
gen = (x**2 for x in range(1000))
first_ten = list(itertools.islice(gen, 10))  # [0,1,4,9,16,25,36,49,64,81]

# groupby — group consecutive same-key elements
data = [("A", 1), ("A", 2), ("B", 3), ("B", 4), ("A", 5)]
for key, group in itertools.groupby(data, key=lambda x: x[0]):
    print(key, list(group))
# A [('A', 1), ('A', 2)]  |  B [('B', 3), ('B', 4)]  |  A [('A', 5)]

✅ Beginner tab complete — check your understanding

  • I can use itertools.chain() to flatten a list of lists
  • I can use itertools.islice() to take the first N items from any iterator
  • I can use itertools.product() to generate all combinations of multiple sequences

Continue to sys →

🔵 Intermediate

combinations, permutations, groupby, and accumulate

What you'll learn: combinations and permutations for mathematical set operations. groupby for grouping consecutive elements. accumulate for running totals.

How to read this tab: The groupby gotcha is critical: your data MUST be sorted by the key before grouping. Try it without sorting first to see the wrong result, then sort and see the correct one.

⏱ 30 min 📄 2 sections 🔶 Prerequisite: Beginner tab
📎

These replace common loop patterns. takewhile(pred, iterable) yields items while the predicate is true, then stops. dropwhile skips items until the predicate is false. filterfalse is the opposite of filter(). accumulate([1,2,3,4]) gives [1,3,6,10] (running total).

Functional itertools: takewhile, dropwhile, filterfalse, accumulate

Pythonitertools_functional.py
import itertools
import operator

# takewhile — yield items while condition is True
nums = [1, 2, 3, 4, 5, 4, 3, 2, 1]
ascending = list(itertools.takewhile(lambda x: x < 5, nums))
print(ascending)   # [1, 2, 3, 4]

# dropwhile — skip items while condition is True, then yield rest
rest = list(itertools.dropwhile(lambda x: x < 5, nums))
print(rest)   # [5, 4, 3, 2, 1]

# filterfalse — yield items where predicate is False
evens = list(itertools.filterfalse(lambda x: x % 2, range(10)))
print(evens)   # [0, 2, 4, 6, 8]

# accumulate — running accumulation (default: sum)
running_sum = list(itertools.accumulate([1, 2, 3, 4, 5]))
print(running_sum)   # [1, 3, 6, 10, 15]

# Running max
running_max = list(itertools.accumulate([3,1,4,1,5,9,2], func=max))
print(running_max)   # [3, 3, 4, 4, 5, 9, 9]

# Running product
factorial = list(itertools.accumulate(range(1,6), func=operator.mul))
print(factorial)   # [1, 2, 6, 24, 120]
💡

The official docs include a "Recipes" section. These are not in itertools itself but are short combinations of itertools functions that solve common problems: pairwise(), sliding window, batched chunking, etc. Python 3.12 added many of these as actual itertools functions (itertools.batched, itertools.pairwise).

Recipes from itertools docs

Pythonitertools_recipes.py
import itertools

# pairwise (Python 3.10+) — overlapping pairs
from itertools import pairwise
pairs = list(pairwise([1, 2, 3, 4]))
print(pairs)   # [(1,2), (2,3), (3,4)]

# batched (Python 3.12+) — batch into n-size chunks
from itertools import batched
pages = list(batched(range(10), 3))
print(pages)   # [(0,1,2), (3,4,5), (6,7,8), (9,)]

# Classic recipe: sliding window
def sliding_window(iterable, n):
    it = iter(iterable)
    window = tuple(itertools.islice(it, n))
    if len(window) == n:
        yield window
    for x in it:
        window = window[1:] + (x,)
        yield window

print(list(sliding_window([1,2,3,4,5], 3)))
# [(1,2,3), (2,3,4), (3,4,5)]
Commonly confused
itertools.groupby() only groups consecutive equal elements — it does not group all elements with the same key. If the input is [A, A, B, A], you get three groups, not two. Sort the input by key first if you want all same-key elements together.
All itertools functions return lazy iterators — they do not materialise results. itertools.chain(a, b) returns an iterator; nothing is computed until you iterate. Wrap in list() to materialise, but only when you need all values — lazy is memory-efficient for large sequences.
itertools.product() is equivalent to nested for loops, not zip. product([1,2],[3,4]) gives (1,3),(1,4),(2,3),(2,4) — all 4 combinations. zip([1,2],[3,4]) gives (1,3),(2,4) — paired by position.

✅ Intermediate tab complete — check your understanding

  • I know the difference between combinations (order doesn't matter) and permutations (order matters)
  • I know that groupby only groups consecutive equal elements — data must be sorted first
  • I can use accumulate() to create a running total without writing a loop

Continue to sys →

🔴 Expert

Infinite iterators and the iterator algebra

What you'll learn: Infinite iterators (cycle, repeat, count) for lazy generation. The concept of iterator algebra — composing simple iterators into complex transformations. The official itertools recipes.

How to read this tab: Always pair infinite iterators with islice() or takewhile() — otherwise they run forever.

⏱ 20 min 📄 2 sections 🔶 Prerequisite: After Stage 3
📎

The power of itertools is composition. Instead of loading everything into memory, you compose lazy iterators: sum(x*x for x in islice(count(1), 1000000)) computes the sum of the first million squares without building a list. Memory use stays constant regardless of input size.

itertools and the iterator algebra

The itertools documentation describes the module as "a set of fast, memory efficient tools that are useful by themselves or in combination" and calls the patterns from the module "iterator algebra." Every itertools function returns a lazy iterator that consumes its inputs one element at a time — composing them builds pipelines that never materialise large intermediate lists. This model is equivalent to Haskell's lazy list combinators and inspired by Clojure's transducers. The Python docs include a recipes section (docs.python.org/3/library/itertools.html#itertools-recipes) with implementations of pairwise, batched, sliding_window, and ~20 other common patterns.

✅ Expert tab complete

  • I can use itertools.cycle() with islice() to create a repeating sequence of finite length
  • I understand iterator algebra: composing simple iterators avoids intermediate lists

Continue to sys →

Sources

1
Python Standard Library — itertools. docs.python.org/3/library/itertools.html.
2
Python itertools recipes. docs.python.org/3/library/itertools.html#itertools-recipes.
Source confidence: High Last verified: Primary source: docs.python.org/3/library/itertools.html

Sources

1
Python Standard Library — itertools. docs.python.org/3/library/itertools.html.
2
Python itertools recipes. docs.python.org/3/library/itertools.html#itertools-recipes.