itertools provides building blocks for efficient looping. Here are the most useful functions.
Why itertools?
- Memory efficient (lazy evaluation)
- Fast (implemented in C)
- Composable (build complex iterators from simple ones)
Combining Iterables
chain
Combine multiple iterables into one:
from itertools import chain
list1 = [1, 2, 3]
list2 = [4, 5, 6]
list3 = [7, 8, 9]
combined = chain(list1, list2, list3)
list(combined) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Flatten nested lists
nested = [[1, 2], [3, 4], [5, 6]]
flat = list(chain.from_iterable(nested)) # [1, 2, 3, 4, 5, 6]zip_longest
Like zip, but continues until longest iterable:
from itertools import zip_longest
names = ["Alice", "Bob"]
scores = [95, 87, 92]
list(zip_longest(names, scores, fillvalue="N/A"))
# [('Alice', 95), ('Bob', 87), ('N/A', 92)]Infinite Iterators
count
Infinite counter:
from itertools import count
for i in count(start=10, step=2):
if i > 20:
break
print(i) # 10, 12, 14, 16, 18, 20cycle
Repeat forever:
from itertools import cycle
colors = cycle(["red", "green", "blue"])
for _, color in zip(range(5), colors):
print(color) # red, green, blue, red, greenrepeat
Repeat a value:
from itertools import repeat
list(repeat("x", 3)) # ['x', 'x', 'x']
# Useful with map
list(map(pow, range(5), repeat(2))) # [0, 1, 4, 9, 16]Filtering
takewhile / dropwhile
from itertools import takewhile, dropwhile
numbers = [1, 3, 5, 7, 2, 4, 6]
# Take while condition is true
list(takewhile(lambda x: x < 6, numbers)) # [1, 3, 5]
# Drop while condition is true
list(dropwhile(lambda x: x < 6, numbers)) # [7, 2, 4, 6]filterfalse
Opposite of filter:
from itertools import filterfalse
numbers = range(10)
list(filterfalse(lambda x: x % 2, numbers)) # [0, 2, 4, 6, 8]compress
Filter using a selector:
from itertools import compress
data = ["a", "b", "c", "d"]
selectors = [True, False, True, False]
list(compress(data, selectors)) # ['a', 'c']Grouping
groupby
Group consecutive elements:
from itertools import groupby
data = [
{"type": "fruit", "name": "apple"},
{"type": "fruit", "name": "banana"},
{"type": "vegetable", "name": "carrot"},
{"type": "vegetable", "name": "broccoli"},
]
# Must be sorted by key first!
for key, group in groupby(data, key=lambda x: x["type"]):
print(key, list(group))
# fruit [{'type': 'fruit', 'name': 'apple'}, ...]
# vegetable [{'type': 'vegetable', 'name': 'carrot'}, ...]Important: groupby only groups consecutive elements. Sort first!
Slicing
islice
Slice an iterator:
from itertools import islice
# Take first 5
list(islice(count(), 5)) # [0, 1, 2, 3, 4]
# Skip first 2, take next 3
list(islice(range(10), 2, 5)) # [2, 3, 4]
# Every other element
list(islice(range(10), 0, None, 2)) # [0, 2, 4, 6, 8]Combinatorics
product
Cartesian product:
from itertools import product
colors = ["red", "blue"]
sizes = ["S", "M", "L"]
list(product(colors, sizes))
# [('red', 'S'), ('red', 'M'), ('red', 'L'),
# ('blue', 'S'), ('blue', 'M'), ('blue', 'L')]
# Repeat same iterable
list(product("AB", repeat=2))
# [('A', 'A'), ('A', 'B'), ('B', 'A'), ('B', 'B')]permutations
All orderings:
from itertools import permutations
list(permutations("ABC", 2))
# [('A', 'B'), ('A', 'C'), ('B', 'A'),
# ('B', 'C'), ('C', 'A'), ('C', 'B')]
list(permutations([1, 2, 3]))
# [(1, 2, 3), (1, 3, 2), (2, 1, 3),
# (2, 3, 1), (3, 1, 2), (3, 2, 1)]combinations
Choose without replacement:
from itertools import combinations
list(combinations("ABCD", 2))
# [('A', 'B'), ('A', 'C'), ('A', 'D'),
# ('B', 'C'), ('B', 'D'), ('C', 'D')]combinations_with_replacement
Choose with replacement:
from itertools import combinations_with_replacement
list(combinations_with_replacement("AB", 2))
# [('A', 'A'), ('A', 'B'), ('B', 'B')]Accumulating
accumulate
Running totals (and more):
from itertools import accumulate
import operator
# Running sum
list(accumulate([1, 2, 3, 4])) # [1, 3, 6, 10]
# Running product
list(accumulate([1, 2, 3, 4], operator.mul)) # [1, 2, 6, 24]
# Running max
list(accumulate([3, 1, 4, 1, 5, 9], max)) # [3, 3, 4, 4, 5, 9]Practical Examples
Batch processing
from itertools import islice
def batched(iterable, n):
"""Yield n-sized chunks."""
it = iter(iterable)
while batch := list(islice(it, n)):
yield batch
list(batched(range(10), 3))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]Pairwise iteration
from itertools import pairwise # Python 3.10+
list(pairwise([1, 2, 3, 4]))
# [(1, 2), (2, 3), (3, 4)]
# Pre-3.10
from itertools import tee
def pairwise_compat(iterable):
a, b = tee(iterable)
next(b, None)
return zip(a, b)Generate test data
from itertools import product
# All combinations of parameters
params = list(product(
[True, False], # enabled
[1, 10, 100], # limit
["asc", "desc"], # order
))
# 12 test casesQuick Reference
from itertools import (
# Combining
chain, # Flatten iterables
zip_longest, # Zip with fill value
# Infinite
count, # 0, 1, 2, ...
cycle, # Repeat sequence
repeat, # Repeat value
# Filtering
takewhile, # Take while true
dropwhile, # Drop while true
filterfalse, # Opposite of filter
compress, # Filter by selector
# Grouping/Slicing
groupby, # Group consecutive
islice, # Slice iterator
# Combinatorics
product, # Cartesian product
permutations, # All orderings
combinations, # Choose without replacement
combinations_with_replacement,
# Accumulating
accumulate, # Running totals
)itertools functions are lazy—they only compute values as needed. Chain them together to build powerful data pipelines.
React to this post: