Course outline · 0% complete

0/27 lessons0%

Course overview →

itertools: ready-made iteration tools

lesson 6-2 · ~11 min · 16/27

Ready-made iterator recipes

Every engineer eventually needs "all pairs of players", "these two lists glued together", or "the first 100 of an endless sequence". Writing those as nested index loops is slow to read and easy to get wrong, so the standard library ships them pre-built and pre-tested. itertools is that toolbox: lazy iterator builders, the same laziness you built by hand with generators in lesson 4-2.

These are the ones you will reach for most.

ToolWhat it yields
chain(a, b)all of a, then all of b
product(a, b)every pairing, like nested loops
combinations(a, 2)each unordered pair, no repeats
permutations(a, 2)each ordered pair
islice(it, 5)first 5 items of any iterator
count(10)10, 11, 12, ... forever

Because the results are lazy iterators, wrap them in list(...) to look at them, or loop over them directly.

islice(it, 5) stops asking after five items, which makes it the safe way to sample an infinite iterator like count. A plain list(count(10)) would try to build an endless list and never finish.

Building a round-robin schedule

combinations gives you every pair of players exactly once, never pairing anyone with themselves and never repeating a pairing in the other order, which is precisely what a round-robin schedule needs.

from itertools import combinations, chain, islice, count

players = ["mia", "leo", "zoe"]
for a, b in combinations(players, 2):
    print(a, "vs", b)

print(list(chain([1, 2], [3, 4])))
print(list(islice(count(100), 4)))

Output

mia vs leo
mia vs zoe
leo vs zoe
[1, 2, 3, 4]
[100, 101, 102, 103]

Three players produce three matches, not nine. The last line is the important one to notice: count(100) never ends, yet the program finishes, because islice stops requesting values once it has four.

Every size and color pairing

product produces every combination across two sequences, in the order nested loops would produce them, with the first argument as the outer loop.

from itertools import product

sizes = ["S", "M"]
colors = ["red", "blue"]

for size, color in product(sizes, colors):
    print(size, color)

Output

S red
S blue
M red
M blue

product(sizes, colors) is the same set of pairings you got from a two-clause comprehension in lesson 1-3, and it yields each result as a tuple, which the for line unpacks straight into size and color. Two sizes times two colors gives four rows, and the count multiplies out the same way for longer inputs.

Counting combinations against permutations

list(combinations([1, 2, 3, 4], 2)) contains 6 items: (1,2), (1,3), (1,4), (2,3), (2,4), and (3,4).

That is the binomial count C(4,2), the number of ways to choose 2 things from 4 when order does not matter. permutations([1, 2, 3, 4], 2) would give 12 instead, because there order does matter and (1,2) and (2,1) count separately.

Choosing teammates or pairs where the ordering carries no meaning: combinations. Choosing a first place and a second place: permutations.

Slicing an infinite stream

list(islice(count(1), 3)) evaluates to [1, 2, 3]. count(1) yields 1, 2, 3, and onward without end, and islice cuts the stream off after three items.

Laziness is the whole trick. The infinite iterator never computes a fourth value, because nothing ever asks for one. The same pattern lets you take a peek at the first few results of an expensive pipeline without paying to run all of it.