The itertools module provides memory-efficient combinatoric iterators. Generate all possible arrangements without materializing them in memory.

Permutations

All possible orderings:

from itertools import permutations
 
# All permutations of a sequence
items = ['a', 'b', 'c']
for p in permutations(items):
    print(p)
# ('a', 'b', 'c')
# ('a', 'c', 'b')
# ('b', 'a', 'c')
# ('b', 'c', 'a')
# ('c', 'a', 'b')
# ('c', 'b', 'a')
 
# Permutations of length r
for p in permutations(items, r=2):
    print(p)
# ('a', 'b')
# ('a', 'c')
# ('b', 'a')
# ('b', 'c')
# ('c', 'a')
# ('c', 'b')

Combinations

Subsets without regard to order:

from itertools import combinations
 
items = ['a', 'b', 'c', 'd']
 
# All 2-element combinations
for c in combinations(items, 2):
    print(c)
# ('a', 'b')
# ('a', 'c')
# ('a', 'd')
# ('b', 'c')
# ('b', 'd')
# ('c', 'd')
 
# All 3-element combinations
list(combinations(items, 3))
# [('a', 'b', 'c'), ('a', 'b', 'd'), ('a', 'c', 'd'), ('b', 'c', 'd')]

Combinations with Replacement

Allow repeated elements:

from itertools import combinations_with_replacement
 
items = ['a', 'b', 'c']
 
for c in combinations_with_replacement(items, 2):
    print(c)
# ('a', 'a')
# ('a', 'b')
# ('a', 'c')
# ('b', 'b')
# ('b', 'c')
# ('c', 'c')

Cartesian Product

All possible pairs from multiple iterables:

from itertools import product
 
# Two iterables
colors = ['red', 'blue']
sizes = ['S', 'M', 'L']
 
for item in product(colors, sizes):
    print(item)
# ('red', 'S')
# ('red', 'M')
# ('red', 'L')
# ('blue', 'S')
# ('blue', 'M')
# ('blue', 'L')
 
# Three or more iterables
list(product([0, 1], [0, 1], [0, 1]))
# [(0,0,0), (0,0,1), (0,1,0), (0,1,1), (1,0,0), (1,0,1), (1,1,0), (1,1,1)]
 
# Repeat same iterable
list(product([0, 1], repeat=3))
# Same as product([0,1], [0,1], [0,1])

Counting Combinations

from itertools import permutations, combinations, product
from math import factorial, comb
 
items = ['a', 'b', 'c', 'd']
n = len(items)
r = 2
 
# Permutations: n! / (n-r)!
num_perms = len(list(permutations(items, r)))
formula = factorial(n) // factorial(n - r)
print(f"Permutations: {num_perms} = {formula}")  # 12
 
# Combinations: n! / (r! * (n-r)!)
num_combs = len(list(combinations(items, r)))
formula = comb(n, r)  # Python 3.8+
print(f"Combinations: {num_combs} = {formula}")  # 6
 
# Product: n^r (for repeat=r with n items)
num_products = len(list(product(items, repeat=r)))
print(f"Products: {num_products} = {n**r}")  # 16

Password Generator

from itertools import product
import string
 
def generate_passwords(length: int, charset: str = None):
    """Generate all possible passwords of given length."""
    if charset is None:
        charset = string.ascii_lowercase + string.digits
    
    for combo in product(charset, repeat=length):
        yield ''.join(combo)
 
# Generate 3-character passwords
for i, pwd in enumerate(generate_passwords(3, 'abc')):
    print(pwd)
    if i >= 10:  # Limit output
        break
# aaa, aab, aac, aba, abb, abc, aca, acb, acc, baa, bab

Subset Generation

from itertools import combinations
 
def all_subsets(items):
    """Generate all subsets (power set)."""
    for r in range(len(items) + 1):
        yield from combinations(items, r)
 
for subset in all_subsets(['a', 'b', 'c']):
    print(subset)
# ()
# ('a',)
# ('b',)
# ('c',)
# ('a', 'b')
# ('a', 'c')
# ('b', 'c')
# ('a', 'b', 'c')

Team Assignment

from itertools import combinations
 
def assign_teams(players: list, team_size: int):
    """Generate all possible team combinations."""
    return combinations(players, team_size)
 
players = ['Alice', 'Bob', 'Carol', 'Dave', 'Eve']
teams_of_2 = list(assign_teams(players, 2))
print(f"Possible 2-person teams: {len(teams_of_2)}")  # 10
 
for team in teams_of_2:
    print(f"Team: {', '.join(team)}")

Configuration Testing

from itertools import product
 
def test_configurations():
    """Test all configuration combinations."""
    databases = ['postgres', 'mysql', 'sqlite']
    cache_modes = ['enabled', 'disabled']
    log_levels = ['debug', 'info', 'error']
    
    for db, cache, log in product(databases, cache_modes, log_levels):
        config = {'database': db, 'cache': cache, 'log_level': log}
        run_test(config)
 
# Tests 3 × 2 × 3 = 18 configurations

Grid Coordinates

from itertools import product
 
# Generate 2D grid coordinates
def grid_coords(width: int, height: int):
    return product(range(width), range(height))
 
for x, y in grid_coords(3, 3):
    print(f"({x}, {y})")
 
# 3D grid
for x, y, z in product(range(2), repeat=3):
    print(f"({x}, {y}, {z})")

Anagram Finder

from itertools import permutations
 
def find_anagrams(word: str, dictionary: set) -> list:
    """Find all valid anagrams of a word."""
    anagrams = set()
    for perm in permutations(word):
        candidate = ''.join(perm)
        if candidate in dictionary and candidate != word:
            anagrams.add(candidate)
    return sorted(anagrams)
 
dictionary = {'cat', 'act', 'tac', 'dog', 'god'}
print(find_anagrams('cat', dictionary))  # ['act', 'tac']

Pair Generation

from itertools import combinations
 
def generate_pairs(items: list):
    """Generate all unique pairs."""
    return list(combinations(items, 2))
 
people = ['Alice', 'Bob', 'Carol', 'Dave']
pairs = generate_pairs(people)
print(f"Number of pairs: {len(pairs)}")  # 6
 
for p1, p2 in pairs:
    print(f"{p1} meets {p2}")

Tournament Brackets

from itertools import combinations
 
def round_robin_schedule(teams: list) -> list:
    """Generate round-robin tournament schedule."""
    matches = list(combinations(teams, 2))
    return matches
 
teams = ['Team A', 'Team B', 'Team C', 'Team D']
schedule = round_robin_schedule(teams)
print(f"Total matches: {len(schedule)}")
 
for match_num, (t1, t2) in enumerate(schedule, 1):
    print(f"Match {match_num}: {t1} vs {t2}")

Binary Strings

from itertools import product
 
def binary_strings(length: int) -> list:
    """Generate all binary strings of given length."""
    return [''.join(bits) for bits in product('01', repeat=length)]
 
print(binary_strings(3))
# ['000', '001', '010', '011', '100', '101', '110', '111']
 
# As integers
def binary_values(bits: int) -> list:
    return [int(''.join(b), 2) for b in product('01', repeat=bits)]
 
print(binary_values(3))  # [0, 1, 2, 3, 4, 5, 6, 7]

Memory Efficiency

from itertools import permutations
import sys
 
# Iterators don't store all values
items = list(range(10))
 
# This would be 3,628,800 tuples in memory
# perm_list = list(permutations(items))  # ~300MB
 
# Iterator uses constant memory
perm_iter = permutations(items)
print(sys.getsizeof(perm_iter))  # ~100 bytes
 
# Process one at a time
for p in perm_iter:
    process(p)
    break  # Stop after first

Complexity Reference

FunctionCountFormula
permutations(n)n!factorial(n)
permutations(n, r)nPrfactorial(n) // factorial(n-r)
combinations(n, r)nCrcomb(n, r)
combinations_with_replacement(n, r)comb(n+r-1, r)
product(n, repeat=r)n^rn ** r

Itertools combinatorics generate possibilities lazily—essential when working with large sets where materializing all combinations would exhaust memory.

React to this post: