itertools is Python’s standard library module for building iterator pipelines. Every function returns a lazy iterator — nothing is computed until you loop.

from itertools import *

Infinite Iterators

count(start=0, step=1)

Counts forever from start, adding step each time.

for i in count(10, 2):  # 10, 12, 14, 16 ...
    if i > 20: break

cycle(iterable)

Repeats the iterable endlessly.

colors = cycle(["red", "green", "blue"])
next(colors)  # red
next(colors)  # green
next(colors)  # blue
next(colors)  # red ...

repeat(object, times=None)

Yields object repeatedly, optionally times times.

list(repeat(42, 3))  # [42, 42, 42]

Finite Iterators — Terminating on Shortest

accumulate(iterable, func=operator.add)

Yields running totals (or results of a binary function).

list(accumulate([1, 2, 3, 4]))          # [1, 3, 6, 10]
list(accumulate([1, 2, 3, 4], mul))     # [1, 2, 6, 24]
list(accumulate([3, 1, 4, 1, 5], max))  # [3, 3, 4, 4, 5]

chain(*iterables)

Concatenates iterables — like + for iterators.

list(chain([1, 2], [3, 4], [5]))  # [1, 2, 3, 4, 5]

chain.from_iterable(iterable_of_iterables)

Flatten one level.

list(chain.from_iterable([[1, 2], [3], [4, 5]]))  # [1, 2, 3, 4, 5]

compress(data, selectors)

Filters data where corresponding selectors element is truthy.

list(compress("ABCDEF", [1, 0, 1, 0, 1, 1]))  # ['A', 'C', 'E', 'F']

dropwhile(predicate, iterable)

Drops elements while predicate is true, then yields everything after.

list(dropwhile(lambda x: x < 5, [1, 4, 6, 3, 7]))
# [6, 3, 7]  — stops checking after first False

filterfalse(predicate, iterable)

Like filter but keeps elements where predicate is false.

list(filterfalse(is_even, [1, 2, 3, 4]))  # [1, 3]

groupby(iterable, key=None)

Groups consecutive elements by key. The iterable must be sorted by the same keygroupby doesn’t sort for you.

items = [("a", 1), ("a", 2), ("b", 3), ("b", 4)]
for key, group in groupby(items, lambda x: x[0]):
    print(key, list(group))
# a [(a,1), (a,2)]
# b [(b,3), (b,4)]

islice(iterable, stop) / islice(iterable, start, stop, step=1)

Lazy slicing — like [start:stop:step] but on any iterable.

list(islice(range(100), 5))          # [0, 1, 2, 3, 4]
list(islice(range(100), 10, 20, 2))  # [10, 12, 14, 16, 18]

pairwise(iterable)

Yields overlapping pairs (Python 3.10+).

list(pairwise("ABCD"))  # [('A','B'), ('B','C'), ('C','D')]

starmap(func, iterable_of_tuples)

Like map but unpacks each tuple as *args.

list(starmap(pow, [(2, 3), (3, 2), (5, 2)]))  # [8, 9, 25]

takewhile(predicate, iterable)

Yields elements while predicate is true, stops at first False.

list(takewhile(lambda x: x < 5, [1, 4, 6, 3, 7]))
# [1, 4]

tee(iterable, n=2)

Creates n independent clones of an iterator. The original should not be used after tee.

it1, it2 = tee(range(3))
list(it1)  # [0, 1, 2]
list(it2)  # [0, 1, 2]

zip_longest(*iterables, fillvalue=None)

Like zip but pads shorter iterables with fillvalue instead of stopping.

list(zip_longest("AB", "xyz", fillvalue="-"))
# [('A', 'x'), ('B', 'y'), ('-', 'z')]

Combinatoric Iterators

product(*iterables, repeat=1)

Cartesian product — nested loops as an iterator.

list(product("AB", "12"))
# [('A','1'), ('A','2'), ('B','1'), ('B','2')]

list(product("AB", repeat=2))
# [('A','A'), ('A','B'), ('B','A'), ('B','B')]

permutations(iterable, r=None)

All possible orderings of length r (defaults to full length).

list(permutations("ABC", 2))
# [('A','B'), ('A','C'), ('B','A'), ('B','C'), ('C','A'), ('C','B')]

combinations(iterable, r)

All length-r combinations in lexicographic order (no repeats, order doesn’t matter).

list(combinations("ABC", 2))
# [('A','B'), ('A','C'), ('B','C')]

combinations_with_replacement(iterable, r)

Like combinations but elements can repeat.

list(combinations_with_replacement("ABC", 2))
# [('A','A'), ('A','B'), ('A','C'), ('B','B'), ('B','C'), ('C','C')]

Quick Reference Table

Function Input Output Lazy Python
count start, step ∞ numbers yes 2.2+
cycle iterable ∞ repeats yes 2.2+
repeat obj[, n] n copies (∞ if no n) yes 2.2+
accumulate iterable[, func] running totals yes 3.2+
chain *iters concatenation yes 2.2+
chain.from_iterable iter of iters flatten 1 level yes 2.6+
compress data, selectors filtered by selector yes 3.1+
dropwhile pred, iterable drops while True yes 2.2+
filterfalse pred, iterable keeps where False yes 2.2+
groupby iterable[, key] key, sub-iterators yes 2.2+
islice iter, [start,] stop[, step] lazy slice yes 2.2+
pairwise iterable overlapping pairs yes 3.10+
starmap func, pairs func(*pair) yes 2.2+
takewhile pred, iterable yields while True yes 2.2+
tee iterable, n n independent clones yes 2.2+
zip_longest *iters[, fillvalue] zip padded yes 2.6+
product *iters[, repeat] cartesian product yes 2.6+
permutations iterable, r r-length orderings yes 2.6+
combinations iterable, r r-length combos yes 2.6+
combinations_with_replacement iterable, r r-length combos w/ repeats yes 2.6+

Real-World Patterns

Chunking a list

def chunked(iterable, n):
    it = iter(iterable)
    return iter(lambda: list(islice(it, n)), [])

list(chunked(range(10), 3))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

Partitioning by predicate

def partition(pred, iterable):
    t1, t2 = tee(iterable)
    return list(filterfalse(pred, t1)), list(filter(pred, t2))

evens, odds = partition(lambda x: x % 2 == 0, range(10))

Nested loop flattening

matrix = [[1, 2], [3, 4], [5, 6]]
flat = list(chain.from_iterable(matrix))  # [1, 2, 3, 4, 5, 6]

Unique elements in insertion order

def unique(iterable):
    seen = set()
    return (x for x in iterable if x not in seen and not seen.add(x))

Sliding window

def sliding_window(iterable, n):
    iters = tee(iterable, n)
    for i, it in enumerate(iters):
        next(islice(it, i, i), None)
    return zip(*iters)

list(sliding_window("ABCDEF", 3))
# [('A','B','C'), ('B','C','D'), ('C','D','E'), ('D','E','F')]

Golden rule: itertools functions compose beautifully. chain.from_iterable(map(func, data)), groupby(sorted(data), key), islice(cycle(colors), n) — build pipelines, not loops.