itertools recipes and more-itertools

itertools · Expert · 11 min

The recipes show expert-level composition, and the more-itertools package ships them ready to use.

The itertools documentation ends with a section of recipes: short functions built from the primitives. Reading them is the fastest way to learn idiomatic, efficient iterator code. A few favourites:

  • sliding_window(it, n): overlapping windows of size n, implemented with a deque(maxlen=n) that yields a tuple after each append.
  • **roundrobin(*iterables)**: take one item from each iterable in turn, dropping exhausted ones; uses cycle over bound __next__ methods and islice to shrink the cycle.
  • unique_everseen(it, key=None): remove duplicates while preserving order, remembering seen keys in a set.
  • partition(pred, it): split into false and true items with tee and filterfalse/filter.
  • consume(it, n=None): advance an iterator by n steps, or exhaust it, at C speed using deque(it, maxlen=0) or next(islice(...)).
  • flatten, nth, all_equal, first_true, before_and_after, iter_index...

Recipes evolve with Python versions; 3.12 and 3.13 docs updated several to use batched and newer features.

The third-party more-itertools package implements all the recipes plus many more (chunked, peekable, spy, windowed, bucket), tested and documented. In application code, depending on it is usually better than copying recipes around.

Example

from collections import deque
from itertools import cycle, filterfalse, islice, tee

def sliding_window(iterable, n):
    it = iter(iterable)
    window = deque(islice(it, n - 1), maxlen=n)
    for x in it:
        window.append(x)
        yield tuple(window)

def roundrobin(*iterables):
    iterators = map(iter, iterables)
    for num_active in range(len(iterables), 0, -1):
        iterators = cycle(islice(iterators, num_active))
        yield from map(next, iterators)    # stops at the first exhausted one
def partition(pred, iterable):
    t1, t2 = tee(iterable)
    return filterfalse(pred, t1), filter(pred, t2)

print(list(sliding_window("abcd", 3)))   # [('a', 'b', 'c'), ('b', 'c', 'd')]
print(list(roundrobin("ABC", "D", "EF")))  # ['A', 'D', 'E', 'B', 'F', 'C']
odd, even = partition(lambda x: x % 2 == 0, range(6))
print(list(odd), list(even))             # [1, 3, 5] [0, 2, 4]
it = iter(range(10)); deque(islice(it, 4), maxlen=0)   # consume 4 items
print(next(it))                          # 4

Remember

  • The docs' recipes show idiomatic compositions of the primitives
  • deque(it, maxlen=0) consumes an iterator at C speed
  • Use the more-itertools package instead of copying recipes

Watch out for

  • Copying an old recipe from a blog that differs from the current docs' tested version.

Check yourself

  1. Why does the consume recipe use deque(iterator, maxlen=0)?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/itertools.html#itertools-recipes

Builds on

Unlocks

Nothing builds on this yet.