The itertools module in the Python standard library provides a collection of fast, memory-efficient tools for working with iterators. Use itertools to combine, filter, and transform sequences without writing deeply nested loops or building intermediate lists.
from itertools import chain
letters = ["a", "b"]
numbers = [1, 2, 3]
combined = list(chain(letters, numbers))
print(combined) # ['a', 'b', 1, 2, 3]chain concatenates multiple iterables into a single iterator. The result is computed on demand; list() materializes it for printing.
Infinite iterators
Three itertools functions produce infinite sequences that never run out of values on their own, so they need to be paired with something that stops them. Use islice to take a limited slice from the front of an infinite iterator.
from itertools import count, cycle, repeat, islice
print(list(islice(count(10, 2), 5))) # [10, 12, 14, 16, 18]
print(list(islice(cycle("AB"), 6))) # ['A', 'B', 'A', 'B', 'A', 'B']
print(list(repeat("Hi", 3))) # ['Hi', 'Hi', 'Hi']count(start, step) counts up from start by step. cycle(seq) repeats a sequence endlessly. repeat(value, n) repeats a value n times.
Always pair infinite iterators with islice or a loop condition.
Combining iterables
These functions combine multiple iterables in different ways, from simple concatenation to pairing elements position by position.
from itertools import chain, zip_longest
list_a = [1, 2, 3]
list_b = ["a", "b"]
print(list(chain(list_a, list_b))) # [1, 2, 3, 'a', 'b']
print(list(zip(list_a, list_b))) # [(1, 'a'), (2, 'b')]
print(list(zip_longest(list_a, list_b, fillvalue=None))) # [(1, 'a'), (2, 'b'), (3, None)]zip_longest pairs elements from both iterables, filling missing values with fillvalue when one iterable is shorter. By contrast, built-in zip() stops at the shortest iterable.
product: Cartesian products
product generates every ordered combination of items from the input iterables, the same result you would get from writing one nested for loop per iterable.
from itertools import product
ranks = ["A", "K", "Q"]
suits = ["hearts", "spades"]
for rank, suit in product(ranks, suits):
print(f"{rank} of {suit}")Each rank is paired with every suit in turn, producing all six rank-suit combinations in order, the same way a full deck's worth of card labels would be generated:
A of hearts
A of spades
K of hearts
K of spades
Q of hearts
Q of spadesUse repeat=N to take the Cartesian product of an iterable with itself N times, which is a compact way to generate every fixed-length combination of a small alphabet:
from itertools import product
print(list(product("01", repeat=3)))Every 3-bit combination of "0" and "1" is generated, in the same order three nested loops over the same two characters would produce:
[('0', '0', '0'), ('0', '0', '1'), ('0', '1', '0'), ('0', '1', '1'),
('1', '0', '0'), ('1', '0', '1'), ('1', '1', '0'), ('1', '1', '1')]product("01", repeat=3) generates all 3-bit binary strings. This is equivalent to three nested loops.
combinations and permutations
These functions generate subsets and arrangements from an iterable, covering the three most common combinatorics needs: unordered subsets, ordered arrangements, and subsets that allow repeats.
from itertools import combinations, permutations, combinations_with_replacement
items = ["A", "B", "C"]
print(list(combinations(items, 2)))
print(list(permutations(items, 2)))
print(list(combinations_with_replacement(items, 2)))combinations skips reversed pairs like (B, A), permutations includes them, and combinations_with_replacement adds pairs of the same item like (A, A):
[('A', 'B'), ('A', 'C'), ('B', 'C')]
[('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
[('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'B'), ('B', 'C'), ('C', 'C')]| Function | Order matters? | Allows repeats? | Example output |
|---|---|---|---|
| combinations(items, 2) | No | No | (A,B), (A,C), (B,C) |
| permutations(items, 2) | Yes | No | (A,B), (A,C), (B,A), ... |
| combinations_with_replacement(items, 2) | No | Yes | (A,A), (A,B), (A,C), ... |
groupby: grouping consecutive elements
groupby groups consecutive elements that share the same key. The input must be sorted by the key you are grouping on.
from itertools import groupby
logs = [
("ERROR", "Disk full"),
("INFO", "Server started"),
("ERROR", "Timeout"),
("INFO", "User login"),
]
for level, entries in groupby(sorted(logs), key=lambda x: x[0]):
print(f"{level}:")
for entry in entries:
print(f" {entry[1]}")Sorting the logs first groups every ERROR entry together and every INFO entry together, regardless of their original order in the list:
ERROR:
Disk full
Timeout
INFO:
Server started
User logingroupby returns (key, group_iterator) pairs. The group iterator yields consecutive elements with the same key. If the input is not sorted, disconnected runs with the same key become separate groups.
Slicing and filtering iterators
islice slices an iterator by position, the same way list slicing works but without needing the whole sequence in memory first. dropwhile and takewhile slice by condition instead of position.
from itertools import islice, dropwhile, takewhile
nums = range(20)
print(list(islice(nums, 5, 10, 2))) # [5, 7, 9]
values = [1, 3, 5, 2, 4, 6]
print(list(takewhile(lambda x: x < 4, values))) # [1, 3]
print(list(dropwhile(lambda x: x < 4, values))) # [5, 2, 4, 6]takewhile yields elements while the condition is true, then stops. dropwhile skips elements while the condition is true, then yields the rest.
accumulate: running totals
accumulate yields accumulated results. By default it sums, but you can pass any binary function.
from itertools import accumulate
import operator
nums = [1, 2, 3, 4, 5]
print(list(accumulate(nums))) # [1, 3, 6, 10, 15]
print(list(accumulate(nums, operator.mul))) # [1, 2, 6, 24, 120]accumulate(nums) produces running sums. accumulate(nums, operator.mul) produces running products. This is useful for cumulative statistics, running totals, or any progressive computation.
Practical example: generating test data
Use itertools to generate combinations of test parameters, covering every browser, screen size, and theme combination without writing three nested loops by hand.
from itertools import product
browsers = ["chrome", "firefox", "safari"]
sizes = ["mobile", "tablet", "desktop"]
themes = ["light", "dark"]
test_cases = list(product(browsers, sizes, themes))
print(f"Total test cases: {len(test_cases)}")
for browser, size, theme in test_cases[:3]:
print(f" {browser} / {size} / {theme}")3 browsers times 3 sizes times 2 themes gives 18 total combinations, and the loop prints just the first three to keep the output short:
Total test cases: 18
chrome / mobile / light
chrome / mobile / dark
chrome / tablet / lightproduct(browsers, sizes, themes) generates every combination of browser, screen size, and theme. This pattern is useful for parameterized testing, configuration matrix generation, and scenario planning.
Common mistakes
Using groupby on unsorted data. groupby only groups consecutive matching elements. Sort the iterable by the same key you plan to group on, or use defaultdict(list) from collections for unsorted grouping.
Forgetting that itertools produce iterators. combinations("ABC", 2) returns an iterator, not a list. Call list() if you need to index, slice, or iterate multiple times.
Materializing large combinations. product(range(1000), repeat=3) generates a billion tuples. Never call list() on a huge combinatorial result. Iterate lazily instead.
Using accumulate with the wrong operator. The default is operator.add. Pass operator.mul, max, min, or a custom function for non-additive accumulation.
Rune AI
Key Insights
itertools.chain(*iterables)concatenates multiple iterables into a single iterator.itertools.product(A, B)generates the Cartesian product of two or more iterables.itertools.combinations(seq, r)generates all r-length combinations without repeats.itertools.groupby(seq, key)groups consecutive elements by a key function.itertools.islice(seq, start, stop, step)slices an iterator like list slicing.- All itertools functions return iterators; wrap with
list()to materialize results.
Frequently Asked Questions
When should I use itertools instead of a for loop?
What is the difference between product and combinations?
Are itertools iterators reusable?
Conclusion
itertools is the standard library module for efficient, memory-friendly iteration. Use chain to flatten iterables, product for Cartesian products, combinations and permutations for combinatorics, and groupby for grouping sorted data. Each function returns an iterator, so results are computed on demand.
More in this topic
Format Dates and Times in Python
Learn how to use strftime and strptime to format dates as strings and parse date strings into Python datetime objects.
What Is the Python Standard Library?
Learn what the Python standard library is, why it matters, and how to use its built-in modules without installing anything extra.
Encode and Decode Base64 Data in Python
Learn how to use Python's base64 module to encode binary data as text and decode Base64 strings back to bytes.