Focused practice · 9 min read

Python itertools Practice Guide: Lazy Streams

itertools is a toolbox for small iterator operations. The functions do not try to solve a whole problem; they compose. Once you recognize count, islice, chain, and groupby, many loops become shorter and easier to read.

Bound an infinite stream

count produces values forever. That is useful only when another function limits the request. islice takes a prefix without turning the whole stream into a list. The result is still lazy, so wrap it when you need to inspect it.

from itertools import count, islice
print(list(islice(count(10), 3)))

A finite window

Join streams with chain

chain reads its inputs in order. It does not sort or deduplicate. That makes it a direct replacement for a loop that appends one list to another. If the inputs are large, chain avoids building the combined list up front.

from itertools import chain
print(list(chain([1, 2], ["a", "b"])))

One stream from two

Group only adjacent keys

groupby emits a new group whenever the key changes. It does not search the entire input for matching keys. Sort by the same key before grouping when equal values are not adjacent. Consume each group before moving on.

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))

Consecutive groups

Sort before grouping

The grouping key and the sort key must agree. A stable sort preserves the original order within equal keys, which is often what you want. After sorting, groupby can see all equal keys as one run.

from itertools import groupby
data = [("b", 1), ("a", 2), ("a", 3)]
sorted_data = sorted(data, key=lambda pair: pair[0])
print([(key, len(list(group))) for key, group in groupby(sorted_data, key=lambda pair: pair[0])])

Sort, then group

Materialize deliberately

Most itertools results are iterators. They can be consumed once and do not support indexing. Convert to a list when you need to print, count, or traverse more than once. Do it at the boundary, not in the middle of every stage.

from itertools import chain
result = list(chain([1], [2, 3]))
print(result)
print(result[1])

A list at the boundary

Your practice checklist

Common questions

Does groupby sort the data?

No. It groups adjacent items only. Sort by the same key first if equal keys may be separated.

Can chain combine strings?

Yes. It iterates each string character by character, just like a for loop over each string.

Why is my itertools result empty?

You probably consumed the iterator already. Iterators are single-use; create a new one or materialize a list.

Practice itertools

12 questions on count, islice, chain, groupby, and lazy iterator behavior.

Open practice

Keep reading: