Need to generate all possible password combinations, batch process millions of records, or create a sliding window over streaming data? The itertools module provides memory-efficient building blocks for these iterator patterns, implemented in C for speed.

The itertools module provides efficient iterator building blocks for creating custom iterators and working with sequences.

Why Use Itertools?

  • Memory efficient (lazy evaluation)
  • Fast (implemented in C)
  • Combinatoric operations
  • Infinite iterators
  • Iterator algebra

Infinite Iterators

infinite_iterators.py
Replay: real traced execution (multi-file project)
# Infinite iterators

from itertools import count, cycle, repeat

# count: infinite counter
print("count(10, 2) - first 5:")
counter = count(10, 2)  # Start at 10, step by 2
for _ in range(5):
    print(next(counter), end=" ")
print()

# cycle: infinite cycle through iterable
print("\ncycle(['A', 'B', 'C']) - first 7:")
cycler = cycle(['A', 'B', 'C'])
for _ in range(7):
    print(next(cycler), end=" ")
print()

# repeat: repeat value n times
print("\nrepeat('X', 5):")
for item in repeat('X', 5):
    print(item, end=" ")
print()

# Practical: enumerate with start offset
print("\nUsing count for custom enumerate:")
for i, letter in zip(count(1), ['a', 'b', 'c', 'd']):
    print(f"{i}. {letter}")
  1. counter ← count(10, 2)

    5# count: infinite counter6print("count(10, 2) - first 5:")7counter→ count(10, 2) = count(10, 2)  # Start at 10, step by 28for _ in range(5):
    outputcount(10, 2) - first 5:
  2. counter ← count(12, 2)

    pass 1 of 5
    7counter = count(10, 2)  # Start at 10, step by 28for _0 in range(5):9    print(next(counter→ count(12, 2)), end=" ")10print()
    output10
    All 5 passes — pass 1 is the card above
    pass_counter
    10count(10, 2) count(12, 2)
    21count(12, 2) count(14, 2)
    32count(14, 2) count(16, 2)
    43count(16, 2) count(18, 2)
    54count(18, 2) count(20, 2)
  3. cycler ← ⟨cycle A⟩

    9    print(next(counter), end=" ")10print()1112# cycle: infinite cycle through iterable13print("\ncycle(['A', 'B', 'C']) - first 7:")14cycler→ ⟨cycle A⟩ = cycle(['A', 'B', 'C'])15for _ in range(7):
    output
    cycle(['A', 'B', 'C']) - first 7:
  4. for _ in range(7):

    pass 1 of 7
    14cycler = cycle(['A', 'B', 'C'])15for _0 in range(7):16    print(next(cycler⟨cycle A⟩), end=" ")17print()
    outputA
    All 7 passes — pass 1 is the card above
    pass_
    10
    21
    32
    43
    54
    65
    76
  5. print()

    16    print(next(cycler), end=" ")17print()1819# repeat: repeat value n times20print("\nrepeat('X', 5):")21for item in repeat('X', 5):
    output
    repeat('X', 5):
  6. for item in repeat('X', 5):

    pass 1 of 5
    20print("\nrepeat('X', 5):")21for itemX in repeat('X', 5):22    print(itemX, end=" ")23print()
    outputX
  7. print()

    22    print(item, end=" ")23print()2425# Practical: enumerate with start offset26print("\nUsing count for custom enumerate:")27for i, letter in zip(count(1), ['a', 'b', 'c', 'd']):
    output
    Using count for custom enumerate:
  8. for i, letter in zip(count(1), ['a', 'b', 'c', 'd']):

    pass 1 of 4
    26print("\nUsing count for custom enumerate:")27for i1, lettera in zip(count(1), ['a', 'b', 'c', 'd']):28    print(f"{i1}. {lettera}")
    output1. a
    All 4 passes — pass 1 is the card above
    passiletter
    11a
    22b
    33c
    44d
infinite iterator - an iterator that never ends, like count(), cycle(), and repeat()

Combinatoric Iterators

items
combinatorics.py
Replay: real traced execution (multi-file project)
# Combinations and permutations

from itertools import combinations, permutations, combinations_with_replacement

items = ['A', 'B', 'C']

# Combinations: order doesn't matter, no repetition
print("Combinations of 2:")
for combo in combinations(items, 2):
    print(combo)

# Permutations: order matters, no repetition
print("\nPermutations of 2:")
for perm in permutations(items, 2):
    print(perm)

# Combinations with replacement
print("\nCombinations with replacement:")
for combo in combinations_with_replacement(items, 2):
    print(combo)

# Practical: all 3-digit PIN codes
from itertools import product
print("\nSample PIN codes (first 10):")
pins = product(range(10), repeat=3)
for i, pin in enumerate(pins):
    if i >= 10:
        break
    print(''.join(map(str, pin)))
# Combinations and permutations

from itertools import combinations, permutations, combinations_with_replacement

items = ['X', 'Y', 'Z']

# Combinations: order doesn't matter, no repetition
print("Combinations of 2:")
for combo in combinations(items, 2):
    print(combo)

# Permutations: order matters, no repetition
print("\nPermutations of 2:")
for perm in permutations(items, 2):
    print(perm)

# Combinations with replacement
print("\nCombinations with replacement:")
for combo in combinations_with_replacement(items, 2):
    print(combo)

# Practical: all 3-digit PIN codes
from itertools import product
print("\nSample PIN codes (first 10):")
pins = product(range(10), repeat=3)
for i, pin in enumerate(pins):
    if i >= 10:
        break
    print(''.join(map(str, pin)))
# Combinations and permutations

from itertools import combinations, permutations, combinations_with_replacement

items = ['red', 'blue', 'green']

# Combinations: order doesn't matter, no repetition
print("Combinations of 2:")
for combo in combinations(items, 2):
    print(combo)

# Permutations: order matters, no repetition
print("\nPermutations of 2:")
for perm in permutations(items, 2):
    print(perm)

# Combinations with replacement
print("\nCombinations with replacement:")
for combo in combinations_with_replacement(items, 2):
    print(combo)

# Practical: all 3-digit PIN codes
from itertools import product
print("\nSample PIN codes (first 10):")
pins = product(range(10), repeat=3)
for i, pin in enumerate(pins):
    if i >= 10:
        break
    print(''.join(map(str, pin)))
  1. items ← ['A', 'B', 'C']

    5items→ ['A', 'B', 'C'] = ['A', 'B', 'C']6#@items=['X', 'Y', 'Z'], ['red', 'blue', 'green']78# Combinations: order doesn't matter, no repetition9print("Combinations of 2:")10for combo in combinations(items, 2):
    outputCombinations of 2:
  2. for combo in combinations(items, 2):

    pass 1 of 3
    9print("Combinations of 2:")10for combo('A', 'B') in combinations(items['A', 'B', 'C'], 2):11    print(combo('A', 'B'))
    output('A', 'B')
    All 3 passes — pass 1 is the card above
    passcombo
    1('A', 'B')
    2('A', 'C')
    3('B', 'C')
  3. print(" Permutations of 2:")

    13# Permutations: order matters, no repetition14print("\nPermutations of 2:")15for perm in permutations(items, 2):
    output
    Permutations of 2:
  4. for perm in permutations(items, 2):

    pass 1 of 6
    14print("\nPermutations of 2:")15for perm('A', 'B') in permutations(items['A', 'B', 'C'], 2):16    print(perm('A', 'B'))
    output('A', 'B')
    All 6 passes — pass 1 is the card above
    passperm
    1('A', 'B')
    2('A', 'C')
    3('B', 'A')
    4('B', 'C')
    5('C', 'A')
    6('C', 'B')
  5. print(" Combinations with replacement:")

    18# Combinations with replacement19print("\nCombinations with replacement:")20for combo in combinations_with_replacement(items, 2):
    output
    Combinations with replacement:
  6. for combo in combinations_with_replacement(items, 2):

    pass 1 of 6
    19print("\nCombinations with replacement:")20for combo('A', 'A') in combinations_with_replacement(items['A', 'B', 'C'], 2):21    print(combo('A', 'A'))
    output('A', 'A')
    All 6 passes — pass 1 is the card above
    passcombo
    1('A', 'A')
    2('A', 'B')
    3('A', 'C')
    4('B', 'B')
    5('B', 'C')
    6('C', 'C')
  7. pins ← ⟨product A⟩

    24from itertools import product25print("\nSample PIN codes (first 10):")26pins→ ⟨product A⟩ = product(range(10), repeat=3)27for i, pin in enumerate(pins):
    output
    Sample PIN codes (first 10):
  8. for i, pin in enumerate(pins):

    pass 1 of 11
    26pins = product(range(10), repeat=3)27for i0, pin(0, 0, 0) in enumerate(pins⟨product A⟩):28    if i >= 10:29        break30    print(''.join(map(str, pin(0, 0, 0))))
    output000
    All 11 passes — pass 1 is the card above
    passipin
    10(0, 0, 0)
    21(0, 0, 1)
    32(0, 0, 2)
    43(0, 0, 3)
    54(0, 0, 4)
    65(0, 0, 5)
    76(0, 0, 6)
    87(0, 0, 7)
    98(0, 0, 8)
    109(0, 0, 9)
    1110(0, 1, 0)
  9. if i >= 10:

    27for i, pin in enumerate(pins):28    if i10 >= 10:29        break30    print(''.join(map(str, pin)))
  1. items ← ['X', 'Y', 'Z']

    5items→ ['X', 'Y', 'Z'] = ['X', 'Y', 'Z']67# Combinations: order doesn't matter, no repetition8print("Combinations of 2:")9for combo in combinations(items, 2):
    outputCombinations of 2:
  2. for combo in combinations(items, 2):

    pass 1 of 3
    8print("Combinations of 2:")9for combo('X', 'Y') in combinations(items['X', 'Y', 'Z'], 2):10    print(combo('X', 'Y'))
    output('X', 'Y')
    All 3 passes — pass 1 is the card above
    passcombo
    1('X', 'Y')
    2('X', 'Z')
    3('Y', 'Z')
  3. print(" Permutations of 2:")

    12# Permutations: order matters, no repetition13print("\nPermutations of 2:")14for perm in permutations(items, 2):
    output
    Permutations of 2:
  4. for perm in permutations(items, 2):

    pass 1 of 6
    13print("\nPermutations of 2:")14for perm('X', 'Y') in permutations(items['X', 'Y', 'Z'], 2):15    print(perm('X', 'Y'))
    output('X', 'Y')
    All 6 passes — pass 1 is the card above
    passperm
    1('X', 'Y')
    2('X', 'Z')
    3('Y', 'X')
    4('Y', 'Z')
    5('Z', 'X')
    6('Z', 'Y')
  5. print(" Combinations with replacement:")

    17# Combinations with replacement18print("\nCombinations with replacement:")19for combo in combinations_with_replacement(items, 2):
    output
    Combinations with replacement:
  6. for combo in combinations_with_replacement(items, 2):

    pass 1 of 6
    18print("\nCombinations with replacement:")19for combo('X', 'X') in combinations_with_replacement(items['X', 'Y', 'Z'], 2):20    print(combo('X', 'X'))
    output('X', 'X')
    All 6 passes — pass 1 is the card above
    passcombo
    1('X', 'X')
    2('X', 'Y')
    3('X', 'Z')
    4('Y', 'Y')
    5('Y', 'Z')
    6('Z', 'Z')
  7. pins ← ⟨product A⟩

    23from itertools import product24print("\nSample PIN codes (first 10):")25pins→ ⟨product A⟩ = product(range(10), repeat=3)26for i, pin in enumerate(pins):
    output
    Sample PIN codes (first 10):
  8. for i, pin in enumerate(pins):

    pass 1 of 11
    25pins = product(range(10), repeat=3)26for i0, pin(0, 0, 0) in enumerate(pins⟨product A⟩):27    if i >= 10:28        break29    print(''.join(map(str, pin(0, 0, 0))))
    output000
    All 11 passes — pass 1 is the card above
    passipin
    10(0, 0, 0)
    21(0, 0, 1)
    32(0, 0, 2)
    43(0, 0, 3)
    54(0, 0, 4)
    65(0, 0, 5)
    76(0, 0, 6)
    87(0, 0, 7)
    98(0, 0, 8)
    109(0, 0, 9)
    1110(0, 1, 0)
  9. if i >= 10:

    26for i, pin in enumerate(pins):27    if i10 >= 10:28        break29    print(''.join(map(str, pin)))
  1. items ← ['red', 'blue', 'green']

    5items→ ['red', 'blue', 'green'] = ['red', 'blue', 'green']67# Combinations: order doesn't matter, no repetition8print("Combinations of 2:")9for combo in combinations(items, 2):
    outputCombinations of 2:
  2. for combo in combinations(items, 2):

    pass 1 of 3
    8print("Combinations of 2:")9for combo('red', 'blue') in combinations(items['red', 'blue', 'green'], 2):10    print(combo('red', 'blue'))
    output('red', 'blue')
    All 3 passes — pass 1 is the card above
    passcombo
    1('red', 'blue')
    2('red', 'green')
    3('blue', 'green')
  3. print(" Permutations of 2:")

    12# Permutations: order matters, no repetition13print("\nPermutations of 2:")14for perm in permutations(items, 2):
    output
    Permutations of 2:
  4. for perm in permutations(items, 2):

    pass 1 of 6
    13print("\nPermutations of 2:")14for perm('red', 'blue') in permutations(items['red', 'blue', 'green'], 2):15    print(perm('red', 'blue'))
    output('red', 'blue')
    All 6 passes — pass 1 is the card above
    passperm
    1('red', 'blue')
    2('red', 'green')
    3('blue', 'red')
    4('blue', 'green')
    5('green', 'red')
    6('green', 'blue')
  5. print(" Combinations with replacement:")

    17# Combinations with replacement18print("\nCombinations with replacement:")19for combo in combinations_with_replacement(items, 2):
    output
    Combinations with replacement:
  6. for combo in combinations_with_replacement(items, 2):

    pass 1 of 6
    18print("\nCombinations with replacement:")19for combo('red', 'red') in combinations_with_replacement(items['red', 'blue', 'green'], 2):20    print(combo('red', 'red'))
    output('red', 'red')
    All 6 passes — pass 1 is the card above
    passcombo
    1('red', 'red')
    2('red', 'blue')
    3('red', 'green')
    4('blue', 'blue')
    5('blue', 'green')
    6('green', 'green')
  7. pins ← ⟨product A⟩

    23from itertools import product24print("\nSample PIN codes (first 10):")25pins→ ⟨product A⟩ = product(range(10), repeat=3)26for i, pin in enumerate(pins):
    output
    Sample PIN codes (first 10):
  8. for i, pin in enumerate(pins):

    pass 1 of 11
    25pins = product(range(10), repeat=3)26for i0, pin(0, 0, 0) in enumerate(pins⟨product A⟩):27    if i >= 10:28        break29    print(''.join(map(str, pin(0, 0, 0))))
    output000
    All 11 passes — pass 1 is the card above
    passipin
    10(0, 0, 0)
    21(0, 0, 1)
    32(0, 0, 2)
    43(0, 0, 3)
    54(0, 0, 4)
    65(0, 0, 5)
    76(0, 0, 6)
    87(0, 0, 7)
    98(0, 0, 8)
    109(0, 0, 9)
    1110(0, 1, 0)
  9. if i >= 10:

    26for i, pin in enumerate(pins):27    if i10 >= 10:28        break29    print(''.join(map(str, pin)))
combinatorics - generating all combinations, permutations, or products of elements

Filtering Iterators

filtering_iterators.py
Replay: real traced execution (multi-file project)
# Filtering iterators

from itertools import filterfalse, takewhile, dropwhile, islice

numbers = range(10)

# filterfalse: opposite of filter
print("filterfalse (odd numbers):")
evens = filterfalse(lambda x: x % 2, numbers)
print(list(evens))

# takewhile: take while condition is true
print("\ntakewhile (x < 5):")
result = takewhile(lambda x: x < 5, numbers)
print(list(result))

# dropwhile: drop while condition is true, then take rest
print("\ndropwhile (x < 5):")
result = dropwhile(lambda x: x < 5, numbers)
print(list(result))

# islice: slice iterator
print("\nislice (skip 2, take 4):")
result = islice(numbers, 2, 6)  # Start at 2, stop before 6
print(list(result))

print("\nislice (every other item):")
result = islice(range(20), 0, None, 2)  # Start, stop, step
print(list(result))
  1. numbers ← range(0, 10), evens ← ⟨filterfalse A⟩, result ← ⟨takewhile B⟩

    5numbers→ range(0, 10) = range(10)67# filterfalse: opposite of filter8print("filterfalse (odd numbers):")9evens→ ⟨filterfalse A⟩ = filterfalse(lambda x: x % 2, numbersrange(0, 10))10print(list(evens⟨filterfalse A⟩))1112# takewhile: take while condition is true13print("\ntakewhile (x < 5):")14result→ ⟨takewhile B⟩ = takewhile(lambda x: x < 5, numbersrange(0, 10))15print(list(result⟨takewhile B⟩))1617# dropwhile: drop while condition is true, then take rest18print("\ndropwhile (x < 5):")19result→ ⟨dropwhile C⟩ = dropwhile(lambda x: x < 5, numbersrange(0, 10))20print(list(result⟨dropwhile C⟩))2122# islice: slice iterator23print("\nislice (skip 2, take 4):")24result→ ⟨islice D⟩ = islice(numbersrange(0, 10), 2, 6)  # Start at 2, stop before 625print(list(result⟨islice D⟩))2627print("\nislice (every other item):")28result→ ⟨islice E⟩ = islice(range(20), 0, None, 2)  # Start, stop, step29print(list(result⟨islice E⟩))
    outputfilterfalse (odd numbers):
    [0, 2, 4, 6, 8]
    
    takewhile (x < 5):
    [0, 1, 2, 3, 4]
    
    dropwhile (x < 5):
    [5, 6, 7, 8, 9]
    
    islice (skip 2, take 4):
    [2, 3, 4, 5]
    
    islice (every other item):
    [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
filtering iterators - tools like takewhile, dropwhile, and filterfalse that select elements

Grouping with groupby

groupby_examples.py
Replay: real traced execution (multi-file project)
# Grouping with groupby

from itertools import groupby

# Group consecutive identical items
data = [1, 1, 1, 2, 2, 3, 3, 3, 3, 1, 1]
print("Grouping consecutive numbers:")
for key, group in groupby(data):
    print(f"{key}: {list(group)}")

# Group by custom key
people = [
    ('Alice', 25),
    ('Bob', 30),
    ('Charlie', 25),
    ('David', 30),
    ('Eve', 25)
]

# Must sort first for groupby to work correctly
people_sorted = sorted(people, key=lambda x: x[1])

print("\nGrouping by age:")
for age, group in groupby(people_sorted, key=lambda x: x[1]):
    names = [person[0] for person in group]
    print(f"Age {age}: {names}")

# Count consecutive runs
data = ['A', 'A', 'A', 'B', 'B', 'C', 'A', 'A']
print("\nRun-length encoding:")
for key, group in groupby(data):
    count = len(list(group))
    print(f"{key}: {count}")
  1. data ← [1, 1, 1, 2, 2, 3, 3, 3, 3, 1, 1]

    5# Group consecutive identical items6data→ [1, 1, 1, 2, 2, 3, 3, 3, 3, 1, 1] = [1, 1, 1, 2, 2, 3, 3, 3, 3, 1, 1]7print("Grouping consecutive numbers:")8for key, group in groupby(data):
    outputGrouping consecutive numbers:
  2. for key, group in groupby(data):

    pass 1 of 4
    7print("Grouping consecutive numbers:")8for key1, group⟨_grouper A⟩ in groupby(data[1, 1, 1, 2, 2, 3, 3, 3, 3, 1, 1]):9    print(f"{key1}: {list(group⟨_grouper A⟩)}")
    output1: [1, 1, 1]
    All 4 passes — pass 1 is the card above
    passkeygroup
    11⟨_grouper A⟩
    22⟨_grouper B⟩
    33⟨_grouper A⟩
    41⟨_grouper B⟩
  3. people ← [('Alice', 25), ('Bob', 30), ('Charlie', 25), ('David', 30), ('Eve', 25)]

    11# Group by custom key12people→ [('Alice', 25), ('Bob', 30), ('Charlie', 25), ('David', 30), ('Eve', 25)] = [13    ('Alice', 25),14    ('Bob', 30),15    ('Charlie', 25),16    ('David', 30),17    ('Eve', 25)18]1920# Must sort first for groupby to work correctly21people_sorted→ [('Alice', 25), ('Charlie', 25), ('Eve', 25), ('Bob', 30), ('David', 30)] = sorted(people[('Alice', 25), ('Bob', 30), ('Charlie', 25), ('David', 30), ('Eve', 25)], key=lambda x: x[1])2223print("\nGrouping by age:")24for age, group in groupby(people_sorted, key=lambda x: x[1]):
    output
    Grouping by age:
  4. names ← ['Alice', 'Charlie', 'Eve']

    pass 1 of 2
    23print("\nGrouping by age:")24for age25, group⟨_grouper C⟩ in groupby(people_sorted[('Alice', 25), ('Charlie', 25), ('Eve', 25), ('Bob', 30), ('David', 30)], key=lambda x: x[1]):25    names→ ['Alice', 'Charlie', 'Eve'] = [person[0](empty) for person in group⟨_grouper C⟩]26    print(f"Age {age25}: {names['Alice', 'Charlie', 'Eve']}")
    outputAge 25: ['Alice', 'Charlie', 'Eve']
  5. names ← ['Bob', 'David']

    pass 2 of 2
    23print("\nGrouping by age:")24for age30, group⟨_grouper B⟩ in groupby(people_sorted[('Alice', 25), ('Charlie', 25), ('Eve', 25), ('Bob', 30), ('David', 30)], key=lambda x: x[1]):25    names→ ['Bob', 'David'] = [person[0](empty) for person in group⟨_grouper B⟩]26    print(f"Age {age30}: {names['Bob', 'David']}")
    outputAge 30: ['Bob', 'David']
  6. data ← ['A', 'A', 'A', 'B', 'B', 'C', 'A', 'A']

    28# Count consecutive runs29data→ ['A', 'A', 'A', 'B', 'B', 'C', 'A', 'A'] = ['A', 'A', 'A', 'B', 'B', 'C', 'A', 'A']30print("\nRun-length encoding:")31for key, group in groupby(data):
    output
    Run-length encoding:
  7. count ← 3

    pass 1 of 4
    30print("\nRun-length encoding:")31for keyA, group⟨_grouper D⟩ in groupby(data['A', 'A', 'A', 'B', 'B', 'C', 'A', 'A']):32    count→ 3 = len(list(group⟨_grouper D⟩))33    print(f"{keyA}: {count3}")
    outputA: 3
    All 4 passes — pass 1 is the card above
    passkeygroupcount
    1A⟨_grouper D⟩3
    2B⟨_grouper E⟩2
    3C⟨_grouper B⟩1
    4A⟨_grouper F⟩2
groupby - groups consecutive elements by a key function (requires sorted input for full grouping)

Chain and Accumulate

chain_accumulate.py
Replay: real traced execution (multi-file project)
# Chain and accumulate

from itertools import chain, accumulate
import operator

# chain: concatenate iterables
list1 = [1, 2, 3]
list2 = [4, 5, 6]
list3 = [7, 8, 9]

print("chain:")
result = chain(list1, list2, list3)
print(list(result))

# chain.from_iterable: flatten nested lists
nested = [[1, 2], [3, 4], [5, 6]]
print("\nchain.from_iterable:")
result = chain.from_iterable(nested)
print(list(result))

# accumulate: running total
numbers = [1, 2, 3, 4, 5]
print("\naccumulate (sum):")
result = accumulate(numbers)
print(list(result))

print("\naccumulate (product):")
result = accumulate(numbers, operator.mul)
print(list(result))

print("\naccumulate (max):")
values = [3, 4, 6, 2, 1, 9, 0, 7, 5]
result = accumulate(values, max)
print(list(result))
  1. list1 ← [1, 2, 3], list2 ← [4, 5, 6], list3 ← [7, 8, 9], result ← ⟨chain A⟩

    6# chain: concatenate iterables7list1→ [1, 2, 3] = [1, 2, 3]8list2→ [4, 5, 6] = [4, 5, 6]9list3→ [7, 8, 9] = [7, 8, 9]1011print("chain:")12result→ ⟨chain A⟩ = chain(list1[1, 2, 3], list2[4, 5, 6], list3[7, 8, 9])13print(list(result⟨chain A⟩))1415# chain.from_iterable: flatten nested lists16nested→ [[1, 2], [3, 4], [5, 6]] = [[1, 2], [3, 4], [5, 6]]17print("\nchain.from_iterable:")18result→ ⟨chain B⟩ = chain<class 'itertools.chain'>.from_iterable(nested[[1, 2], [3, 4], [5, 6]])19print(list(result⟨chain B⟩))2021# accumulate: running total22numbers→ [1, 2, 3, 4, 5] = [1, 2, 3, 4, 5]23print("\naccumulate (sum):")24result→ ⟨accumulate C⟩ = accumulate(numbers[1, 2, 3, 4, 5])25print(list(result⟨accumulate C⟩))2627print("\naccumulate (product):")28result→ ⟨accumulate D⟩ = accumulate(numbers[1, 2, 3, 4, 5], operator.mul<built-in function mul>)29print(list(result⟨accumulate D⟩))3031print("\naccumulate (max):")32values→ [3, 4, 6, 2, 1, 9, 0, 7, 5] = [3, 4, 6, 2, 1, 9, 0, 7, 5]33result→ ⟨accumulate E⟩ = accumulate(values[3, 4, 6, 2, 1, 9, 0, 7, 5], max)34print(list(result⟨accumulate E⟩))
    outputchain:
    [1, 2, 3, 4, 5, 6, 7, 8, 9]
    
    chain.from_iterable:
    [1, 2, 3, 4, 5, 6]
    
    accumulate (sum):
    [1, 3, 6, 10, 15]
    
    accumulate (product):
    [1, 2, 6, 24, 120]
    
    accumulate (max):
    [3, 4, 6, 6, 6, 9, 9, 9, 9]
chain - concatenates multiple iterables into one seamless iterator

Practical Batch Processing

practical.py
Replay: real traced execution (multi-file project)
# Practical example: batch processing with itertools

from itertools import islice, chain, groupby

def chunked(iterable, size):
    """Split iterable into chunks of given size"""
    iterator = iter(iterable)
    while True:
        chunk = list(islice(iterator, size))
        if not chunk:
            break
        yield chunk

# Process data in batches
data = range(1, 26)
print("Processing in batches of 5:")
for batch_num, batch in enumerate(chunked(data, 5), 1):
    print(f"Batch {batch_num}: {batch}")

# Flatten and group
nested_data = [
    ['apple', 'apricot', 'avocado'],
    ['banana', 'blueberry'],
    ['cherry', 'cranberry']
]

print("\nFlattened fruits:")
all_fruits = list(chain.from_iterable(nested_data))
print(all_fruits)

print("\nGrouped by first letter:")
all_fruits.sort()
for letter, fruits in groupby(all_fruits, key=lambda x: x[0]):
    print(f"{letter}: {list(fruits)}")

# Pairwise iteration
def pairwise(iterable):
    """s -> (s0,s1), (s1,s2), (s2,s3), ..."""
    a, b = iter(iterable), iter(iterable)
    next(b, None)
    return zip(a, b)

numbers = [1, 2, 3, 4, 5]
print("\nPairwise iteration:")
for pair in pairwise(numbers):
    print(pair)
  1. data ← range(1, 26)

    14# Process data in batches15data→ range(1, 26) = range(1, 26)16print("Processing in batches of 5:")17for batch_num, batch in enumerate(chunked(data, 5), 1):
    outputProcessing in batches of 5:
  2. iterator ← ⟨range_iterator A⟩

    5def chunked(iterablerange(1, 26), size5):6    """Split iterable into chunks of given size"""7    iterator→ ⟨range_iterator A⟩ = iter(iterablerange(1, 26))8    while True:
  3. chunk ← [1, 2, 3, 4, 5]

    pass 1 of 6
    7iterator = iter(iterable)8while True:9    chunk→ [1, 2, 3, 4, 5] = list(islice(iterator⟨range_iterator A⟩, size5))10    if not chunk:11        break12    yield chunk[1, 2, 3, 4, 5]
    All 6 passes — pass 1 is the card above
    passchunk
    1[1, 2, 3, 4, 5]
    2[6, 7, 8, 9, 10]
    3[11, 12, 13, 14, 15]
    4[16, 17, 18, 19, 20]
    5[21, 22, 23, 24, 25]
    6[]
  4. for batch_num, batch in enumerate(chunked(data, 5), 1):

    pass 1 of 5
    11            break12        yield chunk[1, 2, 3, 4, 5]1314# Process data in batches15data = range(1, 26)16print("Processing in batches of 5:")17for batch_num1, batch[1, 2, 3, 4, 5] in enumerate(chunked(datarange(1, 26), 5), 1):18    print(f"Batch {batch_num1}: {batch[1, 2, 3, 4, 5]}")
    outputBatch 1: [1, 2, 3, 4, 5]
    All 5 passes — pass 1 is the card above
    passbatch_numbatchchunk
    11[1, 2, 3, 4, 5][1, 2, 3, 4, 5]
    22[6, 7, 8, 9, 10][6, 7, 8, 9, 10]
    33[11, 12, 13, 14, 15][11, 12, 13, 14, 15]
    44[16, 17, 18, 19, 20][16, 17, 18, 19, 20]
    55[21, 22, 23, 24, 25][21, 22, 23, 24, 25]
  5. if not chunk:

    9chunk = list(islice(iterator, size))10if not chunk[]:11    break12yield chunk
  6. nested_data ← [['apple', 'apricot', 'avocado'], ['banana', 'blueberry'], ['cherry', 'cranberry']]

    20# Flatten and group21nested_data→ [['apple', 'apricot', 'avocado'], ['banana', 'blueberry'], ['cherry', 'cranberry']] = [22    ['apple', 'apricot', 'avocado'],23    ['banana', 'blueberry'],24    ['cherry', 'cranberry']25]2627print("\nFlattened fruits:")28all_fruits→ ['apple', 'apricot', 'avocado', 'banana', 'blueberry', 'cherry', 'cranberry'] = list(chain<class 'itertools.chain'>.from_iterable(nested_data[['apple', 'apricot', 'avocado'], ['banana', 'blueberry'], ['cherry', 'cranberry']]))29print(all_fruits['apple', 'apricot', 'avocado', 'banana', 'blueberry', 'cherry', 'cranberry'])3031print("\nGrouped by first letter:")32all_fruits['apple', 'apricot', 'avocado', 'banana', 'blueberry', 'cherry', 'cranberry'].sort()33for letter, fruits in groupby(all_fruits, key=lambda x: x[0]):
    output
    Flattened fruits:
    ['apple', 'apricot', 'avocado', 'banana', 'blueberry', 'cherry', 'cranberry']
    
    Grouped by first letter:
  7. for letter, fruits in groupby(all_fruits, key=lambda x: x[0]):

    pass 1 of 3
    32all_fruits.sort()33for lettera, fruits⟨_grouper B⟩ in groupby(all_fruits['apple', 'apricot', 'avocado', 'banana', 'blueberry', 'cherry', 'cranberry'], key=lambda x: x[0]):34    print(f"{lettera}: {list(fruits⟨_grouper B⟩)}")
    outputa: ['apple', 'apricot', 'avocado']
    All 3 passes — pass 1 is the card above
    passletterfruits
    1a⟨_grouper B⟩
    2b⟨_grouper C⟩
    3c⟨_grouper D⟩
  8. numbers ← [1, 2, 3, 4, 5]

    43numbers→ [1, 2, 3, 4, 5] = [1, 2, 3, 4, 5]44print("\nPairwise iteration:")45for pair in pairwise(numbers):
    output
    Pairwise iteration:
  9. a ← ⟨list_iterator E⟩, b ← ⟨list_iterator F⟩

    36# Pairwise iteration37def pairwise(iterable[1, 2, 3, 4, 5]):38    """s -> (s0,s1), (s1,s2), (s2,s3), ..."""39    a→ ⟨list_iterator E⟩, b→ ⟨list_iterator F⟩ = iter(iterable[1, 2, 3, 4, 5]), iter(iterable)40    next(b⟨list_iterator F⟩, None)41    return zip(a⟨list_iterator E⟩, b⟨list_iterator F⟩)
  10. for pair in pairwise(numbers):

    pass 1 of 4
    44print("\nPairwise iteration:")45for pair(1, 2) in pairwise(numbers[1, 2, 3, 4, 5]):46    print(pair(1, 2))
    output(1, 2)
    All 4 passes — pass 1 is the card above
    passpair
    1(1, 2)
    2(2, 3)
    3(3, 4)
    4(4, 5)

Exercise: practical.py

Build a log file analyzer using itertools for batching and grouping