Chapters
Python114 chapters

Modules and the standard libraryChapter 76 of 114

itertools

Building blocks for looping, that never build the whole sequence.

Everything here is lazy

itertools functions return iterators. Nothing is computed until you ask, and nothing is stored, so they work on inputs far larger than memory. Wrap in list() to see one.

chain: treat several sequences as one

Python
from itertools import chain

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

groups = [[1, 2], [3], [4, 5]]
print(list(chain.from_iterable(groups)))

Output

[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]

chain.from_iterable is the flattener, and unlike [x for g in groups for x in g] it never builds the intermediate list.

islice: a slice for anything

You cannot write gen[:5] on a generator. islice is the slice that works:

Python
from itertools import islice, count

print(list(islice(count(1), 5)))
print(list(islice("abcdefgh", 2, 6)))
print(list(islice(range(10), 0, 10, 3)))

Output

[1, 2, 3, 4, 5]
['c', 'd', 'e', 'f']
[0, 3, 6, 9]

count(1) is endless. Taking five from it costs five.

The endless three

Python
from itertools import count, cycle, repeat, islice

print(list(islice(count(10, 5), 4)))
print(list(islice(cycle("ab"), 5)))
print(list(repeat("x", 3)))

Output

[10, 15, 20, 25]
['a', 'b', 'a', 'b', 'a']
['x', 'x', 'x']

Always pair count and cycle with something that stops, or the loop never ends.

accumulate: running totals

Python
from itertools import accumulate

print(list(accumulate([1, 2, 3, 4])))
print(list(accumulate([1, 2, 3, 4], max)))
print(list(accumulate([3, 1, 4], initial=0)))

Output

[1, 3, 6, 10]
[1, 2, 3, 4]
[0, 3, 4, 8]

groupby: runs of equal items

The one that surprises people: it groups consecutive items, so sort first if you want all of a kind together.

Python
from itertools import groupby

data = ["apple", "avocado", "banana", "blueberry", "apricot"]

for letter, group in groupby(data, key=lambda w: w[0]):
    print(letter, list(group))

Output

a ['apple', 'avocado']
b ['banana', 'blueberry']
a ['apricot']

a appeared twice, because "apricot" was not next to the others. Sorting by the same key fixes it:

Python
from itertools import groupby

data = ["apple", "avocado", "banana", "apricot"]
data.sort(key=lambda w: w[0])

for letter, group in groupby(data, key=lambda w: w[0]):
    print(letter, list(group))

Output

a ['apple', 'avocado', 'apricot']
b ['banana']

The sort is stable, so inside the a run the words keep the order they had in the original list rather than becoming alphabetical.

Combinations and permutations

Python
from itertools import combinations, permutations, product

print(list(combinations("abc", 2)))
print(list(permutations("abc", 2)))
print(list(product([1, 2], "ab")))

Output

[('a', 'b'), ('a', 'c'), ('b', 'c')]
[('a', 'b'), ('a', 'c'), ('b', 'a'), ('b', 'c'), ('c', 'a'), ('c', 'b')]
[(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]

Combinations ignore order; permutations do not. product is the nested loop: product(a, b) is every pairing, and repeat= gives you a loop of loops.

Python
from itertools import product

print(list(product([0, 1], repeat=2)))

Output

[(0, 0), (0, 1), (1, 0), (1, 1)]

Filtering pairs

Python
from itertools import takewhile, dropwhile, compress

numbers = [1, 2, 3, 10, 1]

print(list(takewhile(lambda n: n < 5, numbers)))
print(list(dropwhile(lambda n: n < 5, numbers)))
print(list(compress("abcd", [1, 0, 1, 0])))

Output

[1, 2, 3]
[10, 1]
['a', 'c']

takewhile stops at the first failure, unlike filter, which checks everything. Note the trailing 1 survived dropwhile: once it starts yielding, it never tests again.

pairwise

Python
from itertools import pairwise

readings = [10, 12, 11, 15]
print(list(pairwise(readings)))
print([b - a for a, b in pairwise(readings)])

Output

[(10, 12), (12, 11), (11, 15)]
[2, -1, 4]

Differences between neighbours, without any index arithmetic.

Test yourself

2 questions

Why does groupby show the same key twice sometimes?

Show the answer

It groups consecutive items, so you must sort first — Sort by the same key you group by, and each key appears once.

How do you take the first five items of an endless generator?

Show the answer

islice(source, 5) — You cannot slice a generator, and list() on an endless one never returns.

Next chapter

os and sys

Talk to the operating system: environment, paths, and the running interpreter.