Beyond the standard itertools functions, the documentation includes recipes—patterns built from itertools primitives.

Sliding Window

from itertools import islice
from collections import deque
 
def sliding_window(iterable, n):
    """sliding_window('ABCDEF', 3) → ABC BCD CDE DEF"""
    it = iter(iterable)
    window = deque(islice(it, n), maxlen=n)
    if len(window) == n:
        yield tuple(window)
    for x in it:
        window.append(x)
        yield tuple(window)
 
# Usage
data = [1, 2, 3, 4, 5, 6]
for window in sliding_window(data, 3):
    print(window)
# (1, 2, 3)
# (2, 3, 4)
# (3, 4, 5)
# (4, 5, 6)

Pairwise (Python 3.10+)

from itertools import pairwise
 
# Built-in since 3.10
list(pairwise('ABCD'))  # [('A', 'B'), ('B', 'C'), ('C', 'D')]
 
# For older versions
def pairwise(iterable):
    a, b = tee(iterable)
    next(b, None)
    return zip(a, b)

Grouper

Split into fixed-size chunks:

from itertools import zip_longest
 
def grouper(iterable, n, fillvalue=None):
    """grouper('ABCDEFG', 3, 'x') → ABC DEF Gxx"""
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)
 
list(grouper('ABCDEFG', 3, 'x'))
# [('A', 'B', 'C'), ('D', 'E', 'F'), ('G', 'x', 'x')]
 
# Without fill value (strict chunks)
def batched(iterable, n):
    """batched('ABCDEFG', 3) → ABC DEF G"""
    from itertools import islice
    it = iter(iterable)
    while batch := tuple(islice(it, n)):
        yield batch
 
list(batched('ABCDEFG', 3))
# [('A', 'B', 'C'), ('D', 'E', 'F'), ('G',)]

Flatten

from itertools import chain
 
def flatten(list_of_lists):
    """Flatten one level of nesting."""
    return chain.from_iterable(list_of_lists)
 
list(flatten([[1, 2], [3, 4], [5]]))  # [1, 2, 3, 4, 5]
 
# Deep flatten
def deep_flatten(nested):
    for item in nested:
        if isinstance(item, (list, tuple)):
            yield from deep_flatten(item)
        else:
            yield item
 
list(deep_flatten([1, [2, [3, 4]], 5]))  # [1, 2, 3, 4, 5]

Unique Elements

from itertools import filterfalse
 
def unique_everseen(iterable, key=None):
    """List unique elements, preserving order."""
    seen = set()
    if key is None:
        for element in filterfalse(seen.__contains__, iterable):
            seen.add(element)
            yield element
    else:
        for element in iterable:
            k = key(element)
            if k not in seen:
                seen.add(k)
                yield element
 
list(unique_everseen('AAAABBBCCDAABBB'))  # ['A', 'B', 'C', 'D']
list(unique_everseen('ABBcCAD', key=str.lower))  # ['A', 'B', 'c', 'D']

Take and Drop

from itertools import islice
 
def take(n, iterable):
    """Return first n items."""
    return list(islice(iterable, n))
 
def drop(n, iterable):
    """Drop first n items."""
    return islice(iterable, n, None)
 
def tail(n, iterable):
    """Return last n items."""
    from collections import deque
    return iter(deque(iterable, maxlen=n))
 
take(3, range(10))       # [0, 1, 2]
list(drop(3, range(10))) # [3, 4, 5, 6, 7, 8, 9]
list(tail(3, range(10))) # [7, 8, 9]

Nth Element

from itertools import islice
 
def nth(iterable, n, default=None):
    """Return nth item or default."""
    return next(islice(iterable, n, None), default)
 
nth(range(10), 3)     # 3
nth(range(10), 100)   # None
nth(range(10), 100, 'missing')  # 'missing'

Consume Iterator

from itertools import islice
from collections import deque
 
def consume(iterator, n=None):
    """Advance iterator n steps (or exhaust if n is None)."""
    if n is None:
        deque(iterator, maxlen=0)
    else:
        next(islice(iterator, n, n), None)
 
it = iter(range(10))
consume(it, 3)
print(next(it))  # 3

Partition

from itertools import filterfalse, tee
 
def partition(predicate, iterable):
    """Split into false and true elements."""
    t1, t2 = tee(iterable)
    return filterfalse(predicate, t1), filter(predicate, t2)
 
is_odd = lambda x: x % 2
evens, odds = partition(is_odd, range(10))
list(evens)  # [0, 2, 4, 6, 8]
list(odds)   # [1, 3, 5, 7, 9]

Round Robin

from itertools import cycle, islice
 
def roundrobin(*iterables):
    """roundrobin('ABC', 'D', 'EF') → A D E B F C"""
    iterators = [iter(it) for it in iterables]
    while iterators:
        for i, it in enumerate(iterators[:]):
            try:
                yield next(it)
            except StopIteration:
                iterators.remove(it)
 
list(roundrobin('ABC', 'D', 'EF'))  # ['A', 'D', 'E', 'B', 'F', 'C']

Powerset

from itertools import chain, combinations
 
def powerset(iterable):
    """powerset([1,2,3]) → () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"""
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1))
 
list(powerset([1, 2, 3]))
# [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]

First True

def first_true(iterable, default=False, predicate=None):
    """Return first true value or default."""
    return next(filter(predicate, iterable), default)
 
first_true([0, '', [], 'hello', 'world'])  # 'hello'
first_true([0, '', []], default='nothing')  # 'nothing'
first_true([1, 3, 5, 4, 2], predicate=lambda x: x % 2 == 0)  # 4

Quantify

def quantify(iterable, predicate=bool):
    """Count how many times predicate is True."""
    return sum(map(predicate, iterable))
 
quantify([True, False, True, True])  # 3
quantify(range(10), lambda x: x > 5)  # 4

All/Any Equal

from itertools import groupby
 
def all_equal(iterable):
    """Check if all elements are equal."""
    g = groupby(iterable)
    return next(g, True) and not next(g, False)
 
all_equal('AAAA')  # True
all_equal('AABA')  # False
all_equal([])      # True

Interleave

from itertools import chain
 
def interleave(*iterables):
    """interleave('ABC', '12') → A 1 B 2 C"""
    return chain.from_iterable(zip(*iterables))
 
list(interleave('ABC', '123'))  # ['A', '1', 'B', '2', 'C', '3']

Practical Example: Data Processing

from itertools import groupby, islice
from operator import itemgetter
 
# Process log entries
logs = [
    {'level': 'ERROR', 'msg': 'Failed'},
    {'level': 'INFO', 'msg': 'Started'},
    {'level': 'ERROR', 'msg': 'Timeout'},
    {'level': 'INFO', 'msg': 'Completed'},
]
 
# Group by level
keyfunc = itemgetter('level')
for level, entries in groupby(sorted(logs, key=keyfunc), keyfunc):
    print(f"{level}: {list(entries)}")
 
# Process in batches
def process_in_batches(items, batch_size=100):
    it = iter(items)
    while batch := list(islice(it, batch_size)):
        process_batch(batch)

Summary

Essential itertools recipes:

  • sliding_window: Rolling windows over data
  • grouper/batched: Fixed-size chunks
  • flatten: Unnest iterables
  • unique_everseen: Deduplicate preserving order
  • partition: Split by predicate
  • powerset: All subsets

These patterns are memory-efficient (lazy evaluation) and compose well.

React to this post: