Iterators & Generators
Itertools Introduction
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}")
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:counter ← count(12, 2)
pass 1 of 57counter = count(10, 2) # Start at 10, step by 28for _0 in range(5):9 print(next(counter→ count(12, 2)), end=" ")10print()output10All 5 passes — pass 1 is the card above pass _counter1 0 count(10, 2) → count(12, 2) 2 1 count(12, 2) → count(14, 2) 3 2 count(14, 2) → count(16, 2) 4 3 count(16, 2) → count(18, 2) 5 4 count(18, 2) → count(20, 2) 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:for _ in range(7):
pass 1 of 714cycler = cycle(['A', 'B', 'C'])15for _0 in range(7):16 print(next(cycler⟨cycle A⟩), end=" ")17print()outputAAll 7 passes — pass 1 is the card above pass _1 0 2 1 3 2 4 3 5 4 6 5 7 6 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):for item in repeat('X', 5):
pass 1 of 520print("\nrepeat('X', 5):")21for itemX in repeat('X', 5):22 print(itemX, end=" ")23print()outputXprint()
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:for i, letter in zip(count(1), ['a', 'b', 'c', 'd']):
pass 1 of 426print("\nUsing count for custom enumerate:")27for i1, lettera in zip(count(1), ['a', 'b', 'c', 'd']):28 print(f"{i1}. {lettera}")output1. aAll 4 passes — pass 1 is the card above pass iletter1 1 a 2 2 b 3 3 c 4 4 d
infinite iterator - an iterator that never ends, like count(), cycle(), and repeat()
Combinatoric Iterators
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)))
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:for combo in combinations(items, 2):
pass 1 of 39print("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 pass combo1 ('A', 'B') 2 ('A', 'C') 3 ('B', 'C') print(" Permutations of 2:")
13# Permutations: order matters, no repetition14print("\nPermutations of 2:")15for perm in permutations(items, 2):output Permutations of 2:for perm in permutations(items, 2):
pass 1 of 614print("\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 pass perm1 ('A', 'B') 2 ('A', 'C') 3 ('B', 'A') 4 ('B', 'C') 5 ('C', 'A') 6 ('C', 'B') print(" Combinations with replacement:")
18# Combinations with replacement19print("\nCombinations with replacement:")20for combo in combinations_with_replacement(items, 2):output Combinations with replacement:for combo in combinations_with_replacement(items, 2):
pass 1 of 619print("\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 pass combo1 ('A', 'A') 2 ('A', 'B') 3 ('A', 'C') 4 ('B', 'B') 5 ('B', 'C') 6 ('C', 'C') 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):for i, pin in enumerate(pins):
pass 1 of 1126pins = 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))))output000All 11 passes — pass 1 is the card above pass ipin1 0 (0, 0, 0) 2 1 (0, 0, 1) 3 2 (0, 0, 2) 4 3 (0, 0, 3) 5 4 (0, 0, 4) 6 5 (0, 0, 5) 7 6 (0, 0, 6) 8 7 (0, 0, 7) 9 8 (0, 0, 8) 10 9 (0, 0, 9) 11 10 (0, 1, 0) if i >= 10:
27for i, pin in enumerate(pins):28 if i10 >= 10:29 break30 print(''.join(map(str, pin)))
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:for combo in combinations(items, 2):
pass 1 of 38print("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 pass combo1 ('X', 'Y') 2 ('X', 'Z') 3 ('Y', 'Z') print(" Permutations of 2:")
12# Permutations: order matters, no repetition13print("\nPermutations of 2:")14for perm in permutations(items, 2):output Permutations of 2:for perm in permutations(items, 2):
pass 1 of 613print("\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 pass perm1 ('X', 'Y') 2 ('X', 'Z') 3 ('Y', 'X') 4 ('Y', 'Z') 5 ('Z', 'X') 6 ('Z', 'Y') print(" Combinations with replacement:")
17# Combinations with replacement18print("\nCombinations with replacement:")19for combo in combinations_with_replacement(items, 2):output Combinations with replacement:for combo in combinations_with_replacement(items, 2):
pass 1 of 618print("\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 pass combo1 ('X', 'X') 2 ('X', 'Y') 3 ('X', 'Z') 4 ('Y', 'Y') 5 ('Y', 'Z') 6 ('Z', 'Z') 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):for i, pin in enumerate(pins):
pass 1 of 1125pins = 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))))output000All 11 passes — pass 1 is the card above pass ipin1 0 (0, 0, 0) 2 1 (0, 0, 1) 3 2 (0, 0, 2) 4 3 (0, 0, 3) 5 4 (0, 0, 4) 6 5 (0, 0, 5) 7 6 (0, 0, 6) 8 7 (0, 0, 7) 9 8 (0, 0, 8) 10 9 (0, 0, 9) 11 10 (0, 1, 0) if i >= 10:
26for i, pin in enumerate(pins):27 if i10 >= 10:28 break29 print(''.join(map(str, pin)))
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:for combo in combinations(items, 2):
pass 1 of 38print("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 pass combo1 ('red', 'blue') 2 ('red', 'green') 3 ('blue', 'green') print(" Permutations of 2:")
12# Permutations: order matters, no repetition13print("\nPermutations of 2:")14for perm in permutations(items, 2):output Permutations of 2:for perm in permutations(items, 2):
pass 1 of 613print("\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 pass perm1 ('red', 'blue') 2 ('red', 'green') 3 ('blue', 'red') 4 ('blue', 'green') 5 ('green', 'red') 6 ('green', 'blue') print(" Combinations with replacement:")
17# Combinations with replacement18print("\nCombinations with replacement:")19for combo in combinations_with_replacement(items, 2):output Combinations with replacement:for combo in combinations_with_replacement(items, 2):
pass 1 of 618print("\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 pass combo1 ('red', 'red') 2 ('red', 'blue') 3 ('red', 'green') 4 ('blue', 'blue') 5 ('blue', 'green') 6 ('green', 'green') 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):for i, pin in enumerate(pins):
pass 1 of 1125pins = 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))))output000All 11 passes — pass 1 is the card above pass ipin1 0 (0, 0, 0) 2 1 (0, 0, 1) 3 2 (0, 0, 2) 4 3 (0, 0, 3) 5 4 (0, 0, 4) 6 5 (0, 0, 5) 7 6 (0, 0, 6) 8 7 (0, 0, 7) 9 8 (0, 0, 8) 10 9 (0, 0, 9) 11 10 (0, 1, 0) 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))
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}")
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:for key, group in groupby(data):
pass 1 of 47print("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 pass keygroup1 1 ⟨_grouper A⟩ 2 2 ⟨_grouper B⟩ 3 3 ⟨_grouper A⟩ 4 1 ⟨_grouper B⟩ 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:names ← ['Alice', 'Charlie', 'Eve']
pass 1 of 223print("\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']names ← ['Bob', 'David']
pass 2 of 223print("\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']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:count ← 3
pass 1 of 430print("\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: 3All 4 passes — pass 1 is the card above pass keygroupcount1 A ⟨_grouper D⟩ 3 2 B ⟨_grouper E⟩ 2 3 C ⟨_grouper B⟩ 1 4 A ⟨_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))
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)
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: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:chunk ← [1, 2, 3, 4, 5]
pass 1 of 67iterator = 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 pass chunk1 [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 [] for batch_num, batch in enumerate(chunked(data, 5), 1):
pass 1 of 511 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 pass batch_numbatchchunk1 1 [1, 2, 3, 4, 5] [1, 2, 3, 4, 5] 2 2 [6, 7, 8, 9, 10] [6, 7, 8, 9, 10] 3 3 [11, 12, 13, 14, 15] [11, 12, 13, 14, 15] 4 4 [16, 17, 18, 19, 20] [16, 17, 18, 19, 20] 5 5 [21, 22, 23, 24, 25] [21, 22, 23, 24, 25] if not chunk:
9chunk = list(islice(iterator, size))10if not chunk[]:11 break12yield chunknested_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:for letter, fruits in groupby(all_fruits, key=lambda x: x[0]):
pass 1 of 332all_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 pass letterfruits1 a ⟨_grouper B⟩ 2 b ⟨_grouper C⟩ 3 c ⟨_grouper D⟩ 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: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⟩)for pair in pairwise(numbers):
pass 1 of 444print("\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 pass pair1 (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