Repeated expensive calculations slow applications, and repetitive function signatures clutter code. The functools module provides caching decorators for performance optimization, partial application for cleaner APIs, and reduction operations for aggregating sequences.

higher-order function A function that takes other functions as arguments or returns functions - the foundation of functional programming patterns.

Reduce Operations

Aggregate a sequence to a single value:

numbers
reduce.py
Replay: real traced execution (multi-file project)
# functools.reduce examples

from functools import reduce

# Basic reduce
print("Basic reduce:")

# Sum with reduce
numbers = [1, 2, 3, 4, 5]
total = reduce(lambda x, y: x + y, numbers)
print(f"Sum of {numbers}: {total}")

# Product with reduce
product = reduce(lambda x, y: x * y, numbers)
print(f"Product of {numbers}: {product}")

# Max with reduce
values = [15, 42, 8, 93, 27]
maximum = reduce(lambda x, y: x if x > y else y, values)
print(f"Max of {values}: {maximum}")

# Reduce with initial value
print("\nReduce with initial value:")

# Sum with initial value
nums = [1, 2, 3, 4]
result = reduce(lambda x, y: x + y, nums, 10)
print(f"Sum of {nums} + 10: {result}")

# Count occurrences
words = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']
count = reduce(lambda acc, word: acc + 1 if word == 'apple' else acc, words, 0)
print(f"Count 'apple' in {words}: {count}")

# String operations
print("\nString operations:")

# Concatenate strings
strings = ['Hello', ' ', 'World', '!']
message = reduce(lambda x, y: x + y, strings)
print(f"Concatenated: {message}")

# Build sentence
words_list = ['Python', 'is', 'awesome']
sentence = reduce(lambda x, y: f"{x} {y}", words_list)
print(f"Sentence: {sentence}")

# List operations
print("\nList operations:")

# Flatten nested lists
nested = [[1, 2], [3, 4], [5, 6]]
flattened = reduce(lambda x, y: x + y, nested)
print(f"Nested: {nested}")
print(f"Flattened: {flattened}")

# Merge dictionaries
dicts = [{'a': 1}, {'b': 2}, {'c': 3}]
merged = reduce(lambda x, y: {**x, **y}, dicts)
print(f"Merged dicts: {merged}")

# Mathematical operations
print("\nMathematical operations:")

# Factorial
n = 5
factorial = reduce(lambda x, y: x * y, range(1, n + 1))
print(f"{n}! = {factorial}")

# Power
base = 2
exponent = 10
power = reduce(lambda x, y: x * y, [base] * exponent)
print(f"{base}^{exponent} = {power}")

# GCD of list
import math
numbers_gcd = [48, 64, 80]
gcd = reduce(math.gcd, numbers_gcd)
print(f"GCD of {numbers_gcd}: {gcd}")

# Custom accumulator
print("\nCustom accumulator:")

# Build histogram
data = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
histogram = reduce(
    lambda acc, x: {**acc, x: acc.get(x, 0) + 1},
    data,
    {}
)
print(f"Data: {data}")
print(f"Histogram: {histogram}")

# Running totals
values_list = [10, 20, 30, 40]
def running_sum(acc, x):
    return acc + [acc[-1] + x]

totals = reduce(running_sum, values_list, [0])
print(f"Values: {values_list}")
print(f"Running totals: {totals}")

# Comparison with alternatives
print("\nComparison with alternatives:")

nums_compare = [1, 2, 3, 4, 5]

# Using reduce
sum_reduce = reduce(lambda x, y: x + y, nums_compare)
print(f"reduce: {sum_reduce}")

# Using sum()
sum_builtin = sum(nums_compare)
print(f"sum():  {sum_builtin}")

# Using loop
sum_loop = 0
for n in nums_compare:
    sum_loop += n
print(f"loop:   {sum_loop}")

# Prefer built-ins when available
print(f"max() is clearer than reduce: {max(nums_compare)}")

# functools.reduce examples

from functools import reduce

# Basic reduce
print("Basic reduce:")

# Sum with reduce
numbers = [2, 4, 6]
total = reduce(lambda x, y: x + y, numbers)
print(f"Sum of {numbers}: {total}")

# Product with reduce
product = reduce(lambda x, y: x * y, numbers)
print(f"Product of {numbers}: {product}")

# Max with reduce
values = [15, 42, 8, 93, 27]
maximum = reduce(lambda x, y: x if x > y else y, values)
print(f"Max of {values}: {maximum}")

# Reduce with initial value
print("\nReduce with initial value:")

# Sum with initial value
nums = [1, 2, 3, 4]
result = reduce(lambda x, y: x + y, nums, 10)
print(f"Sum of {nums} + 10: {result}")

# Count occurrences
words = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']
count = reduce(lambda acc, word: acc + 1 if word == 'apple' else acc, words, 0)
print(f"Count 'apple' in {words}: {count}")

# String operations
print("\nString operations:")

# Concatenate strings
strings = ['Hello', ' ', 'World', '!']
message = reduce(lambda x, y: x + y, strings)
print(f"Concatenated: {message}")

# Build sentence
words_list = ['Python', 'is', 'awesome']
sentence = reduce(lambda x, y: f"{x} {y}", words_list)
print(f"Sentence: {sentence}")

# List operations
print("\nList operations:")

# Flatten nested lists
nested = [[1, 2], [3, 4], [5, 6]]
flattened = reduce(lambda x, y: x + y, nested)
print(f"Nested: {nested}")
print(f"Flattened: {flattened}")

# Merge dictionaries
dicts = [{'a': 1}, {'b': 2}, {'c': 3}]
merged = reduce(lambda x, y: {**x, **y}, dicts)
print(f"Merged dicts: {merged}")

# Mathematical operations
print("\nMathematical operations:")

# Factorial
n = 5
factorial = reduce(lambda x, y: x * y, range(1, n + 1))
print(f"{n}! = {factorial}")

# Power
base = 2
exponent = 10
power = reduce(lambda x, y: x * y, [base] * exponent)
print(f"{base}^{exponent} = {power}")

# GCD of list
import math
numbers_gcd = [48, 64, 80]
gcd = reduce(math.gcd, numbers_gcd)
print(f"GCD of {numbers_gcd}: {gcd}")

# Custom accumulator
print("\nCustom accumulator:")

# Build histogram
data = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
histogram = reduce(
    lambda acc, x: {**acc, x: acc.get(x, 0) + 1},
    data,
    {}
)
print(f"Data: {data}")
print(f"Histogram: {histogram}")

# Running totals
values_list = [10, 20, 30, 40]
def running_sum(acc, x):
    return acc + [acc[-1] + x]

totals = reduce(running_sum, values_list, [0])
print(f"Values: {values_list}")
print(f"Running totals: {totals}")

# Comparison with alternatives
print("\nComparison with alternatives:")

nums_compare = [1, 2, 3, 4, 5]

# Using reduce
sum_reduce = reduce(lambda x, y: x + y, nums_compare)
print(f"reduce: {sum_reduce}")

# Using sum()
sum_builtin = sum(nums_compare)
print(f"sum():  {sum_builtin}")

# Using loop
sum_loop = 0
for n in nums_compare:
    sum_loop += n
print(f"loop:   {sum_loop}")

# Prefer built-ins when available
print(f"max() is clearer than reduce: {max(nums_compare)}")

# functools.reduce examples

from functools import reduce

# Basic reduce
print("Basic reduce:")

# Sum with reduce
numbers = [10, 20, 30]
total = reduce(lambda x, y: x + y, numbers)
print(f"Sum of {numbers}: {total}")

# Product with reduce
product = reduce(lambda x, y: x * y, numbers)
print(f"Product of {numbers}: {product}")

# Max with reduce
values = [15, 42, 8, 93, 27]
maximum = reduce(lambda x, y: x if x > y else y, values)
print(f"Max of {values}: {maximum}")

# Reduce with initial value
print("\nReduce with initial value:")

# Sum with initial value
nums = [1, 2, 3, 4]
result = reduce(lambda x, y: x + y, nums, 10)
print(f"Sum of {nums} + 10: {result}")

# Count occurrences
words = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']
count = reduce(lambda acc, word: acc + 1 if word == 'apple' else acc, words, 0)
print(f"Count 'apple' in {words}: {count}")

# String operations
print("\nString operations:")

# Concatenate strings
strings = ['Hello', ' ', 'World', '!']
message = reduce(lambda x, y: x + y, strings)
print(f"Concatenated: {message}")

# Build sentence
words_list = ['Python', 'is', 'awesome']
sentence = reduce(lambda x, y: f"{x} {y}", words_list)
print(f"Sentence: {sentence}")

# List operations
print("\nList operations:")

# Flatten nested lists
nested = [[1, 2], [3, 4], [5, 6]]
flattened = reduce(lambda x, y: x + y, nested)
print(f"Nested: {nested}")
print(f"Flattened: {flattened}")

# Merge dictionaries
dicts = [{'a': 1}, {'b': 2}, {'c': 3}]
merged = reduce(lambda x, y: {**x, **y}, dicts)
print(f"Merged dicts: {merged}")

# Mathematical operations
print("\nMathematical operations:")

# Factorial
n = 5
factorial = reduce(lambda x, y: x * y, range(1, n + 1))
print(f"{n}! = {factorial}")

# Power
base = 2
exponent = 10
power = reduce(lambda x, y: x * y, [base] * exponent)
print(f"{base}^{exponent} = {power}")

# GCD of list
import math
numbers_gcd = [48, 64, 80]
gcd = reduce(math.gcd, numbers_gcd)
print(f"GCD of {numbers_gcd}: {gcd}")

# Custom accumulator
print("\nCustom accumulator:")

# Build histogram
data = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
histogram = reduce(
    lambda acc, x: {**acc, x: acc.get(x, 0) + 1},
    data,
    {}
)
print(f"Data: {data}")
print(f"Histogram: {histogram}")

# Running totals
values_list = [10, 20, 30, 40]
def running_sum(acc, x):
    return acc + [acc[-1] + x]

totals = reduce(running_sum, values_list, [0])
print(f"Values: {values_list}")
print(f"Running totals: {totals}")

# Comparison with alternatives
print("\nComparison with alternatives:")

nums_compare = [1, 2, 3, 4, 5]

# Using reduce
sum_reduce = reduce(lambda x, y: x + y, nums_compare)
print(f"reduce: {sum_reduce}")

# Using sum()
sum_builtin = sum(nums_compare)
print(f"sum():  {sum_builtin}")

# Using loop
sum_loop = 0
for n in nums_compare:
    sum_loop += n
print(f"loop:   {sum_loop}")

# Prefer built-ins when available
print(f"max() is clearer than reduce: {max(nums_compare)}")

  1. numbers ← [1, 2, 3, 4, 5], total ← 15, product ← 120, values ← [15, 42, 8, 93, 27]

    5# Basic reduce6print("Basic reduce:")78# Sum with reduce9numbers→ [1, 2, 3, 4, 5] = [1, 2, 3, 4, 5]  #@numbers=[2, 4, 6], [10, 20, 30]10total→ 15 = reduce(lambda x, y: x + y, numbers[1, 2, 3, 4, 5])11print(f"Sum of {numbers[1, 2, 3, 4, 5]}: {total15}")1213# Product with reduce14product→ 120 = reduce(lambda x, y: x * y, numbers[1, 2, 3, 4, 5])15print(f"Product of {numbers[1, 2, 3, 4, 5]}: {product120}")1617# Max with reduce18values→ [15, 42, 8, 93, 27] = [15, 42, 8, 93, 27]19maximum→ 93 = reduce(lambda x, y: x if x > y else y, values[15, 42, 8, 93, 27])20print(f"Max of {values[15, 42, 8, 93, 27]}: {maximum93}")2122# Reduce with initial value23print("\nReduce with initial value:")2425# Sum with initial value26nums→ [1, 2, 3, 4] = [1, 2, 3, 4]27result→ 20 = reduce(lambda x, y: x + y, nums[1, 2, 3, 4], 10)28print(f"Sum of {nums[1, 2, 3, 4]} + 10: {result20}")2930# Count occurrences31words→ ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple'] = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']32count→ 3 = reduce(lambda acc, word: acc + 1 if word == 'apple' else acc, words['apple', 'banana', 'apple', 'cherry', 'banana', 'apple'], 0)33print(f"Count 'apple' in {words['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']}: {count3}")3435# String operations36print("\nString operations:")3738# Concatenate strings39strings→ ['Hello', ' ', 'World', '!'] = ['Hello', ' ', 'World', '!']40message→ Hello World! = reduce(lambda x, y: x + y, strings['Hello', ' ', 'World', '!'])41print(f"Concatenated: {messageHello World!}")4243# Build sentence44words_list→ ['Python', 'is', 'awesome'] = ['Python', 'is', 'awesome']45sentence→ Python is awesome = reduce(lambda x, y: f"{x} {y}", words_list['Python', 'is', 'awesome'])46print(f"Sentence: {sentencePython is awesome}")4748# List operations49print("\nList operations:")5051# Flatten nested lists52nested→ [[1, 2], [3, 4], [5, 6]] = [[1, 2], [3, 4], [5, 6]]53flattened→ [1, 2, 3, 4, 5, 6] = reduce(lambda x, y: x + y, nested[[1, 2], [3, 4], [5, 6]])54print(f"Nested: {nested[[1, 2], [3, 4], [5, 6]]}")55print(f"Flattened: {flattened[1, 2, 3, 4, 5, 6]}")5657# Merge dictionaries58dicts→ [{'a': 1}, {'b': 2}, {'c': 3}] = [{'a': 1}, {'b': 2}, {'c': 3}]59merged→ {'a': 1, 'b': 2, 'c': 3} = reduce(lambda x, y: {**x, **y}, dicts[{'a': 1}, {'b': 2}, {'c': 3}])60print(f"Merged dicts: {merged{'a': 1, 'b': 2, 'c': 3}}")6162# Mathematical operations63print("\nMathematical operations:")6465# Factorial66n→ 5 = 567factorial→ 120 = reduce(lambda x, y: x * y, range(1, n5 + 1))68print(f"{n5}! = {factorial120}")6970# Power71base→ 2 = 272exponent→ 10 = 1073power→ 1024 = reduce(lambda x, y: x * y, [base2] * exponent10)74print(f"{base2}^{exponent10} = {power1024}")7576# GCD of list77import math78numbers_gcd→ [48, 64, 80] = [48, 64, 80]79gcd→ 16 = reduce(math.gcd<built-in function gcd>, numbers_gcd[48, 64, 80])80print(f"GCD of {numbers_gcd[48, 64, 80]}: {gcd16}")8182# Custom accumulator83print("\nCustom accumulator:")8485# Build histogram86data→ [1, 2, 2, 3, 3, 3, 4, 4, 4, 4] = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]87histogram→ {1: 1, 2: 2, 3: 3, 4: 4} = reduce(88    lambda acc, x: {**acc, x: acc.get(x, 0) + 1},89    data[1, 2, 2, 3, 3, 3, 4, 4, 4, 4],90    {}91)92print(f"Data: {data[1, 2, 2, 3, 3, 3, 4, 4, 4, 4]}")93print(f"Histogram: {histogram{1: 1, 2: 2, 3: 3, 4: 4}}")9495# Running totals96values_list→ [10, 20, 30, 40] = [10, 20, 30, 40]97def running_sum(acc, x):98    return acc + [acc[-1] + x]99100totals = reduce(running_sum⟨function running_sum A⟩, values_list[10, 20, 30, 40], [0])101print(f"Values: {values_list}")
    outputBasic reduce:
    Sum of [1, 2, 3, 4, 5]: 15
    Product of [1, 2, 3, 4, 5]: 120
    Max of [15, 42, 8, 93, 27]: 93
    
    Reduce with initial value:
    Sum of [1, 2, 3, 4] + 10: 20
    Count 'apple' in ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']: 3
    
    String operations:
    Concatenated: Hello World!
    Sentence: Python is awesome
    
    List operations:
    Nested: [[1, 2], [3, 4], [5, 6]]
    Flattened: [1, 2, 3, 4, 5, 6]
    Merged dicts: {'a': 1, 'b': 2, 'c': 3}
    
    Mathematical operations:
    5! = 120
    2^10 = 1024
    GCD of [48, 64, 80]: 16
    
    Custom accumulator:
    Data: [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
    Histogram: {1: 1, 2: 2, 3: 3, 4: 4}
  2. def running_sum(acc, x):

    pass 1 of 4
    96values_list = [10, 20, 30, 40]97def running_sum(acc[0], x10):98    return acc[0] + [acc[-1]0 + x10]
    All 4 passes — pass 1 is the card above
    passaccxacc[-1]
    1[0]100
    2[0, 10]2010
    3[0, 10, 30]3030
    4[0, 10, 30, 60]4060
  3. totals ← [0, 10, 30, 60, 100], nums_compare ← [1, 2, 3, 4, 5]

    100totals→ [0, 10, 30, 60, 100] = reduce(running_sum⟨function running_sum A⟩, values_list[10, 20, 30, 40], [0])101print(f"Values: {values_list[10, 20, 30, 40]}")102print(f"Running totals: {totals[0, 10, 30, 60, 100]}")103104# Comparison with alternatives105print("\nComparison with alternatives:")106107nums_compare→ [1, 2, 3, 4, 5] = [1, 2, 3, 4, 5]108109# Using reduce110sum_reduce→ 15 = reduce(lambda x, y: x + y, nums_compare[1, 2, 3, 4, 5])111print(f"reduce: {sum_reduce15}")112113# Using sum()114sum_builtin→ 15 = sum(nums_compare[1, 2, 3, 4, 5])115print(f"sum():  {sum_builtin15}")116117# Using loop118sum_loop→ 0 = 0119for n in nums_compare:
    outputValues: [10, 20, 30, 40]
    Running totals: [0, 10, 30, 60, 100]
    
    Comparison with alternatives:
    reduce: 15
    sum():  15
  4. sum_loop ← 1

    pass 1 of 5
    118sum_loop = 0119for n1 in nums_compare[1, 2, 3, 4, 5]:120    sum_loop→ 1 += n1121print(f"loop:   {sum_loop}")
    All 5 passes — pass 1 is the card above
    passnsum_loop
    110 1
    221 3
    333 6
    446 10
    5510 15
  5. print(f"loop: {sum_loop}")

    120    sum_loop += n121print(f"loop:   {sum_loop15}")122123# Prefer built-ins when available124print(f"max() is clearer than reduce: {max(nums_compare[1, 2, 3, 4, 5])}")
    outputloop:   15
    max() is clearer than reduce: 5
  1. numbers ← [2, 4, 6], total ← 12, product ← 48, values ← [15, 42, 8, 93, 27]

    5# Basic reduce6print("Basic reduce:")78# Sum with reduce9numbers→ [2, 4, 6] = [2, 4, 6]10total→ 12 = reduce(lambda x, y: x + y, numbers[2, 4, 6])11print(f"Sum of {numbers[2, 4, 6]}: {total12}")1213# Product with reduce14product→ 48 = reduce(lambda x, y: x * y, numbers[2, 4, 6])15print(f"Product of {numbers[2, 4, 6]}: {product48}")1617# Max with reduce18values→ [15, 42, 8, 93, 27] = [15, 42, 8, 93, 27]19maximum→ 93 = reduce(lambda x, y: x if x > y else y, values[15, 42, 8, 93, 27])20print(f"Max of {values[15, 42, 8, 93, 27]}: {maximum93}")2122# Reduce with initial value23print("\nReduce with initial value:")2425# Sum with initial value26nums→ [1, 2, 3, 4] = [1, 2, 3, 4]27result→ 20 = reduce(lambda x, y: x + y, nums[1, 2, 3, 4], 10)28print(f"Sum of {nums[1, 2, 3, 4]} + 10: {result20}")2930# Count occurrences31words→ ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple'] = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']32count→ 3 = reduce(lambda acc, word: acc + 1 if word == 'apple' else acc, words['apple', 'banana', 'apple', 'cherry', 'banana', 'apple'], 0)33print(f"Count 'apple' in {words['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']}: {count3}")3435# String operations36print("\nString operations:")3738# Concatenate strings39strings→ ['Hello', ' ', 'World', '!'] = ['Hello', ' ', 'World', '!']40message→ Hello World! = reduce(lambda x, y: x + y, strings['Hello', ' ', 'World', '!'])41print(f"Concatenated: {messageHello World!}")4243# Build sentence44words_list→ ['Python', 'is', 'awesome'] = ['Python', 'is', 'awesome']45sentence→ Python is awesome = reduce(lambda x, y: f"{x} {y}", words_list['Python', 'is', 'awesome'])46print(f"Sentence: {sentencePython is awesome}")4748# List operations49print("\nList operations:")5051# Flatten nested lists52nested→ [[1, 2], [3, 4], [5, 6]] = [[1, 2], [3, 4], [5, 6]]53flattened→ [1, 2, 3, 4, 5, 6] = reduce(lambda x, y: x + y, nested[[1, 2], [3, 4], [5, 6]])54print(f"Nested: {nested[[1, 2], [3, 4], [5, 6]]}")55print(f"Flattened: {flattened[1, 2, 3, 4, 5, 6]}")5657# Merge dictionaries58dicts→ [{'a': 1}, {'b': 2}, {'c': 3}] = [{'a': 1}, {'b': 2}, {'c': 3}]59merged→ {'a': 1, 'b': 2, 'c': 3} = reduce(lambda x, y: {**x, **y}, dicts[{'a': 1}, {'b': 2}, {'c': 3}])60print(f"Merged dicts: {merged{'a': 1, 'b': 2, 'c': 3}}")6162# Mathematical operations63print("\nMathematical operations:")6465# Factorial66n→ 5 = 567factorial→ 120 = reduce(lambda x, y: x * y, range(1, n5 + 1))68print(f"{n5}! = {factorial120}")6970# Power71base→ 2 = 272exponent→ 10 = 1073power→ 1024 = reduce(lambda x, y: x * y, [base2] * exponent10)74print(f"{base2}^{exponent10} = {power1024}")7576# GCD of list77import math78numbers_gcd→ [48, 64, 80] = [48, 64, 80]79gcd→ 16 = reduce(math.gcd<built-in function gcd>, numbers_gcd[48, 64, 80])80print(f"GCD of {numbers_gcd[48, 64, 80]}: {gcd16}")8182# Custom accumulator83print("\nCustom accumulator:")8485# Build histogram86data→ [1, 2, 2, 3, 3, 3, 4, 4, 4, 4] = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]87histogram→ {1: 1, 2: 2, 3: 3, 4: 4} = reduce(88    lambda acc, x: {**acc, x: acc.get(x, 0) + 1},89    data[1, 2, 2, 3, 3, 3, 4, 4, 4, 4],90    {}91)92print(f"Data: {data[1, 2, 2, 3, 3, 3, 4, 4, 4, 4]}")93print(f"Histogram: {histogram{1: 1, 2: 2, 3: 3, 4: 4}}")9495# Running totals96values_list→ [10, 20, 30, 40] = [10, 20, 30, 40]97def running_sum(acc, x):98    return acc + [acc[-1] + x]99100totals = reduce(running_sum⟨function running_sum A⟩, values_list[10, 20, 30, 40], [0])101print(f"Values: {values_list}")
    outputBasic reduce:
    Sum of [2, 4, 6]: 12
    Product of [2, 4, 6]: 48
    Max of [15, 42, 8, 93, 27]: 93
    
    Reduce with initial value:
    Sum of [1, 2, 3, 4] + 10: 20
    Count 'apple' in ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']: 3
    
    String operations:
    Concatenated: Hello World!
    Sentence: Python is awesome
    
    List operations:
    Nested: [[1, 2], [3, 4], [5, 6]]
    Flattened: [1, 2, 3, 4, 5, 6]
    Merged dicts: {'a': 1, 'b': 2, 'c': 3}
    
    Mathematical operations:
    5! = 120
    2^10 = 1024
    GCD of [48, 64, 80]: 16
    
    Custom accumulator:
    Data: [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
    Histogram: {1: 1, 2: 2, 3: 3, 4: 4}
  2. def running_sum(acc, x):

    pass 1 of 4
    96values_list = [10, 20, 30, 40]97def running_sum(acc[0], x10):98    return acc[0] + [acc[-1]0 + x10]
    All 4 passes — pass 1 is the card above
    passaccxacc[-1]
    1[0]100
    2[0, 10]2010
    3[0, 10, 30]3030
    4[0, 10, 30, 60]4060
  3. totals ← [0, 10, 30, 60, 100], nums_compare ← [1, 2, 3, 4, 5]

    100totals→ [0, 10, 30, 60, 100] = reduce(running_sum⟨function running_sum A⟩, values_list[10, 20, 30, 40], [0])101print(f"Values: {values_list[10, 20, 30, 40]}")102print(f"Running totals: {totals[0, 10, 30, 60, 100]}")103104# Comparison with alternatives105print("\nComparison with alternatives:")106107nums_compare→ [1, 2, 3, 4, 5] = [1, 2, 3, 4, 5]108109# Using reduce110sum_reduce→ 15 = reduce(lambda x, y: x + y, nums_compare[1, 2, 3, 4, 5])111print(f"reduce: {sum_reduce15}")112113# Using sum()114sum_builtin→ 15 = sum(nums_compare[1, 2, 3, 4, 5])115print(f"sum():  {sum_builtin15}")116117# Using loop118sum_loop→ 0 = 0119for n in nums_compare:
    outputValues: [10, 20, 30, 40]
    Running totals: [0, 10, 30, 60, 100]
    
    Comparison with alternatives:
    reduce: 15
    sum():  15
  4. sum_loop ← 1

    pass 1 of 5
    118sum_loop = 0119for n1 in nums_compare[1, 2, 3, 4, 5]:120    sum_loop→ 1 += n1121print(f"loop:   {sum_loop}")
    All 5 passes — pass 1 is the card above
    passnsum_loop
    110 1
    221 3
    333 6
    446 10
    5510 15
  5. print(f"loop: {sum_loop}")

    120    sum_loop += n121print(f"loop:   {sum_loop15}")122123# Prefer built-ins when available124print(f"max() is clearer than reduce: {max(nums_compare[1, 2, 3, 4, 5])}")
    outputloop:   15
    max() is clearer than reduce: 5
  1. numbers ← [10, 20, 30], total ← 60, product ← 6000, values ← [15, 42, 8, 93, 27]

    5# Basic reduce6print("Basic reduce:")78# Sum with reduce9numbers→ [10, 20, 30] = [10, 20, 30]10total→ 60 = reduce(lambda x, y: x + y, numbers[10, 20, 30])11print(f"Sum of {numbers[10, 20, 30]}: {total60}")1213# Product with reduce14product→ 6000 = reduce(lambda x, y: x * y, numbers[10, 20, 30])15print(f"Product of {numbers[10, 20, 30]}: {product6000}")1617# Max with reduce18values→ [15, 42, 8, 93, 27] = [15, 42, 8, 93, 27]19maximum→ 93 = reduce(lambda x, y: x if x > y else y, values[15, 42, 8, 93, 27])20print(f"Max of {values[15, 42, 8, 93, 27]}: {maximum93}")2122# Reduce with initial value23print("\nReduce with initial value:")2425# Sum with initial value26nums→ [1, 2, 3, 4] = [1, 2, 3, 4]27result→ 20 = reduce(lambda x, y: x + y, nums[1, 2, 3, 4], 10)28print(f"Sum of {nums[1, 2, 3, 4]} + 10: {result20}")2930# Count occurrences31words→ ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple'] = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']32count→ 3 = reduce(lambda acc, word: acc + 1 if word == 'apple' else acc, words['apple', 'banana', 'apple', 'cherry', 'banana', 'apple'], 0)33print(f"Count 'apple' in {words['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']}: {count3}")3435# String operations36print("\nString operations:")3738# Concatenate strings39strings→ ['Hello', ' ', 'World', '!'] = ['Hello', ' ', 'World', '!']40message→ Hello World! = reduce(lambda x, y: x + y, strings['Hello', ' ', 'World', '!'])41print(f"Concatenated: {messageHello World!}")4243# Build sentence44words_list→ ['Python', 'is', 'awesome'] = ['Python', 'is', 'awesome']45sentence→ Python is awesome = reduce(lambda x, y: f"{x} {y}", words_list['Python', 'is', 'awesome'])46print(f"Sentence: {sentencePython is awesome}")4748# List operations49print("\nList operations:")5051# Flatten nested lists52nested→ [[1, 2], [3, 4], [5, 6]] = [[1, 2], [3, 4], [5, 6]]53flattened→ [1, 2, 3, 4, 5, 6] = reduce(lambda x, y: x + y, nested[[1, 2], [3, 4], [5, 6]])54print(f"Nested: {nested[[1, 2], [3, 4], [5, 6]]}")55print(f"Flattened: {flattened[1, 2, 3, 4, 5, 6]}")5657# Merge dictionaries58dicts→ [{'a': 1}, {'b': 2}, {'c': 3}] = [{'a': 1}, {'b': 2}, {'c': 3}]59merged→ {'a': 1, 'b': 2, 'c': 3} = reduce(lambda x, y: {**x, **y}, dicts[{'a': 1}, {'b': 2}, {'c': 3}])60print(f"Merged dicts: {merged{'a': 1, 'b': 2, 'c': 3}}")6162# Mathematical operations63print("\nMathematical operations:")6465# Factorial66n→ 5 = 567factorial→ 120 = reduce(lambda x, y: x * y, range(1, n5 + 1))68print(f"{n5}! = {factorial120}")6970# Power71base→ 2 = 272exponent→ 10 = 1073power→ 1024 = reduce(lambda x, y: x * y, [base2] * exponent10)74print(f"{base2}^{exponent10} = {power1024}")7576# GCD of list77import math78numbers_gcd→ [48, 64, 80] = [48, 64, 80]79gcd→ 16 = reduce(math.gcd<built-in function gcd>, numbers_gcd[48, 64, 80])80print(f"GCD of {numbers_gcd[48, 64, 80]}: {gcd16}")8182# Custom accumulator83print("\nCustom accumulator:")8485# Build histogram86data→ [1, 2, 2, 3, 3, 3, 4, 4, 4, 4] = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]87histogram→ {1: 1, 2: 2, 3: 3, 4: 4} = reduce(88    lambda acc, x: {**acc, x: acc.get(x, 0) + 1},89    data[1, 2, 2, 3, 3, 3, 4, 4, 4, 4],90    {}91)92print(f"Data: {data[1, 2, 2, 3, 3, 3, 4, 4, 4, 4]}")93print(f"Histogram: {histogram{1: 1, 2: 2, 3: 3, 4: 4}}")9495# Running totals96values_list→ [10, 20, 30, 40] = [10, 20, 30, 40]97def running_sum(acc, x):98    return acc + [acc[-1] + x]99100totals = reduce(running_sum⟨function running_sum A⟩, values_list[10, 20, 30, 40], [0])101print(f"Values: {values_list}")
    outputBasic reduce:
    Sum of [10, 20, 30]: 60
    Product of [10, 20, 30]: 6000
    Max of [15, 42, 8, 93, 27]: 93
    
    Reduce with initial value:
    Sum of [1, 2, 3, 4] + 10: 20
    Count 'apple' in ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']: 3
    
    String operations:
    Concatenated: Hello World!
    Sentence: Python is awesome
    
    List operations:
    Nested: [[1, 2], [3, 4], [5, 6]]
    Flattened: [1, 2, 3, 4, 5, 6]
    Merged dicts: {'a': 1, 'b': 2, 'c': 3}
    
    Mathematical operations:
    5! = 120
    2^10 = 1024
    GCD of [48, 64, 80]: 16
    
    Custom accumulator:
    Data: [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
    Histogram: {1: 1, 2: 2, 3: 3, 4: 4}
  2. def running_sum(acc, x):

    pass 1 of 4
    96values_list = [10, 20, 30, 40]97def running_sum(acc[0], x10):98    return acc[0] + [acc[-1]0 + x10]
    All 4 passes — pass 1 is the card above
    passaccxacc[-1]
    1[0]100
    2[0, 10]2010
    3[0, 10, 30]3030
    4[0, 10, 30, 60]4060
  3. totals ← [0, 10, 30, 60, 100], nums_compare ← [1, 2, 3, 4, 5]

    100totals→ [0, 10, 30, 60, 100] = reduce(running_sum⟨function running_sum A⟩, values_list[10, 20, 30, 40], [0])101print(f"Values: {values_list[10, 20, 30, 40]}")102print(f"Running totals: {totals[0, 10, 30, 60, 100]}")103104# Comparison with alternatives105print("\nComparison with alternatives:")106107nums_compare→ [1, 2, 3, 4, 5] = [1, 2, 3, 4, 5]108109# Using reduce110sum_reduce→ 15 = reduce(lambda x, y: x + y, nums_compare[1, 2, 3, 4, 5])111print(f"reduce: {sum_reduce15}")112113# Using sum()114sum_builtin→ 15 = sum(nums_compare[1, 2, 3, 4, 5])115print(f"sum():  {sum_builtin15}")116117# Using loop118sum_loop→ 0 = 0119for n in nums_compare:
    outputValues: [10, 20, 30, 40]
    Running totals: [0, 10, 30, 60, 100]
    
    Comparison with alternatives:
    reduce: 15
    sum():  15
  4. sum_loop ← 1

    pass 1 of 5
    118sum_loop = 0119for n1 in nums_compare[1, 2, 3, 4, 5]:120    sum_loop→ 1 += n1121print(f"loop:   {sum_loop}")
    All 5 passes — pass 1 is the card above
    passnsum_loop
    110 1
    221 3
    333 6
    446 10
    5510 15
  5. print(f"loop: {sum_loop}")

    120    sum_loop += n121print(f"loop:   {sum_loop15}")122123# Prefer built-ins when available124print(f"max() is clearer than reduce: {max(nums_compare[1, 2, 3, 4, 5])}")
    outputloop:   15
    max() is clearer than reduce: 5
reduce() Applies a function cumulatively to sequence items, reducing them to a single value - like folding a list into one result.

Caching with lru_cache

Automatically cache function results:

cache.py
Replay: real traced execution (multi-file project)
# functools.lru_cache examples

from functools import lru_cache
import time

# Basic caching
print("Basic caching:")

@lru_cache(maxsize=128)
def expensive_function(n):
    """Simulate expensive computation."""
    time.sleep(0.001)  # Simulate delay
    return n * n

# First call computes; second call is served from the cache
result1 = expensive_function(10)
result2 = expensive_function(10)
initial_info = expensive_function.cache_info()

print(f"First call:  {result1}")
print(f"Second call: {result2}")
print(f"Cache hits: {initial_info.hits}, misses: {initial_info.misses}")

# Fibonacci with cache
print("\nFibonacci with cache:")

@lru_cache(maxsize=None)  # Unlimited cache
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

result = fibonacci(12)

print(f"fibonacci(12) = {result}")

# Without cache would take much longer
def fib_no_cache(n):
    if n < 2:
        return n
    return fib_no_cache(n - 1) + fib_no_cache(n - 2)

result_no_cache = fib_no_cache(8)  # Much smaller n

print(f"\nWithout cache:")
print(f"fibonacci(8) = {result_no_cache}")

# Cache info
print("\nCache info:")

@lru_cache(maxsize=3)
def compute(x):
    return x * 2

# Make some calls
for i in range(5):
    compute(i)

# Check hits
for i in range(3):
    compute(i)

info = compute.cache_info()
print(f"Hits: {info.hits}")
print(f"Misses: {info.misses}")
print(f"Size: {info.currsize}")
print(f"Max size: {info.maxsize}")

# Cache clear
print("\nCache clear:")

@lru_cache(maxsize=128)
def add(x, y):
    print(f"  Computing {x} + {y}")
    return x + y

print("First calls:")
add(1, 2)
add(3, 4)
add(1, 2)  # Cached

print("\nAfter clearing:")
add.cache_clear()
add(1, 2)  # Not cached anymore

# Maxsize effects
print("\nMaxsize effects:")

@lru_cache(maxsize=2)
def limited(n):
    return n * n

# Fill cache
limited(1)  # Miss
limited(2)  # Miss
limited(1)  # Hit

# Evict oldest
limited(3)  # Miss, evicts 2
limited(1)  # Hit
limited(2)  # Miss (was evicted)

info = limited.cache_info()
print(f"Hits: {info.hits}, Misses: {info.misses}")

# Prime checking with cache
print("\nPrime checking with cache:")

@lru_cache(maxsize=1000)
def is_prime(n):
    if n < 2:
        return False
    if n == 2:
        return True
    if n % 2 == 0:
        return False
    for i in range(3, int(n**0.5) + 1, 2):
        if n % i == 0:
            return False
    return True

# Check primes multiple times
primes = [p for p in range(30) if is_prime(p)]
print(f"Primes < 30: {len(primes)}")

# Recheck (cached)
primes_again = [p for p in range(30) if is_prime(p)]
print(f"Rechecking returns same values: {primes_again == primes}")

info = is_prime.cache_info()
print(f"Cache: {info.hits} hits, {info.misses} misses")

# Distance calculation
print("\nDistance calculation:")

@lru_cache(maxsize=256)
def distance(x1, y1, x2, y2):
    """Calculate Euclidean distance."""
    return ((x2 - x1)**2 + (y2 - y1)**2)**0.5

# Calculate distances
points = [(0, 0), (3, 4), (1, 1), (0, 0), (3, 4)]

for i in range(len(points)):
    for j in range(i + 1, len(points)):
        d = distance(*points[i], *points[j])
        print(f"  {points[i]} to {points[j]}: {d:.2f}")

info = distance.cache_info()
print(f"Cache: {info.hits} hits, {info.misses} misses")

# Memoization pattern
print("\nMemoization pattern:")

@lru_cache(maxsize=None)
def count_paths(m, n):
    """Count paths in m x n grid."""
    if m == 1 or n == 1:
        return 1
    return count_paths(m - 1, n) + count_paths(m, n - 1)

result = count_paths(5, 5)
print(f"Paths in 5x5 grid: {result}")

info = count_paths.cache_info()
print(f"Cache efficiency: {info.hits}/{info.hits + info.misses} hits")

  1. print("Basic caching:")

    6# Basic caching7print("Basic caching:")89@lru_cache(maxsize=128)10def expensive_function(n):11    """Simulate expensive computation."""12    time.sleep(0.001)  # Simulate delay13    return n * n1415# First call computes; second call is served from the cache16result1 = expensive_function(10)17result2 = expensive_function(10)
    outputBasic caching:
  2. def expensive_function(n):

    9@lru_cache(maxsize=128)10def expensive_function(n10):11    """Simulate expensive computation."""12    time<module 'time' (built-in)>.sleep(0.001)  # Simulate delay13    return n10 * n
  3. result1 ← 100, result2 ← 100, initial_info ← CacheInfo(hits=1, misses=1, maxsize=128, currsize=1)

    15# First call computes; second call is served from the cache16result1→ 100 = expensive_function(10)17result2→ 100 = expensive_function(10)18initial_info→ CacheInfo(hits=1, misses=1, maxsize=128, currsize=1) = expensive_function⟨_lru_cache_wrapper A⟩.cache_info()1920print(f"First call:  {result1100}")21print(f"Second call: {result2100}")22print(f"Cache hits: {initial_info.hits1}, misses: {initial_info.misses1}")2324# Fibonacci with cache25print("\nFibonacci with cache:")2627@lru_cache(maxsize=None)  # Unlimited cache28def fibonacci(n):29    if n < 2:30        return n31    return fibonacci(n - 1) + fibonacci(n - 2)3233result = fibonacci(12)
    outputFirst call:  100
    Second call: 100
    Cache hits: 1, misses: 1
    
    Fibonacci with cache:
  4. def fibonacci(n):

    pass 1 of 13
    27@lru_cache(maxsize=None)  # Unlimited cache28def fibonacci(n12):29    if n < 2:30        return n31    return fibonacci(n12 - 1) + fibonacci(n - 2)
    13 passes — pass 1 is the card above
    passn
    112
    211
    310
    49
    58
    67
    76
    85
    94
    ⋯ 2 more passes ⋯
    121
    130
  5. if n < 2:

    pass 1 of 2
    28def fibonacci(n):29    if n1 < 2:30        return n131    return fibonacci(n - 1) + fibonacci(n - 2)
  6. if n < 2:

    pass 2 of 2
    28def fibonacci(n):29    if n0 < 2:30        return n031    return fibonacci(n - 1) + fibonacci(n - 2)
  7. result ← 144

    33result→ 144 = fibonacci(12)3435print(f"fibonacci(12) = {result144}")3637# Without cache would take much longer38def fib_no_cache(n):39    if n < 2:40        return n41    return fib_no_cache(n - 1) + fib_no_cache(n - 2)4243result_no_cache = fib_no_cache(8)  # Much smaller n
    outputfibonacci(12) = 144
  8. def fib_no_cache(n):

    pass 1 of 67
    37# Without cache would take much longer38def fib_no_cache(n8):39    if n < 2:40        return n41    return fib_no_cache(n8 - 1) + fib_no_cache(n - 2)
    67 passes — pass 1 is the card above
    passn
    18
    27
    36
    45
    54
    63
    72
    81
    90
    ⋯ 56 more passes ⋯
    661
    670
  9. if n < 2:

    pass 1 of 34
    38def fib_no_cache(n):39    if n1 < 2:40        return n141    return fib_no_cache(n - 1) + fib_no_cache(n - 2)
    34 passes — pass 1 is the card above
    passn
    11
    20
    31
    41
    50
    61
    70
    81
    91
    ⋯ 23 more passes ⋯
    331
    340
  10. result_no_cache ← 21

    43result_no_cache→ 21 = fib_no_cache(8)  # Much smaller n4445print(f"\nWithout cache:")46print(f"fibonacci(8) = {result_no_cache21}")4748# Cache info49print("\nCache info:")
    output
    Without cache:
    fibonacci(8) = 21
    
    Cache info:
  11. for i in range(5):

    pass 1 of 5
    55# Make some calls56for i0 in range(5):57    compute(i0)
    All 5 passes — pass 1 is the card above
    passi
    10
    21
    32
    43
    54
  12. def compute(x):

    pass 1 of 8
    51@lru_cache(maxsize=3)52def compute(x0):53    return x0 * 2
    All 8 passes — pass 1 is the card above
    passx
    10
    21
    32
    43
    54
    60
    71
    82
  13. compute(i)

    56for i in range(5):57    compute(i0)
  14. compute(i)

    56for i in range(5):57    compute(i1)
  15. compute(i)

    56for i in range(5):57    compute(i2)
  16. compute(i)

    56for i in range(5):57    compute(i3)
  17. compute(i)

    56for i in range(5):57    compute(i4)
  18. for i in range(3):

    pass 1 of 3
    59# Check hits60for i0 in range(3):61    compute(i0)
    All 3 passes — pass 1 is the card above
    passi
    10
    21
    32
  19. compute(i)

    60for i in range(3):61    compute(i0)
  20. compute(i)

    60for i in range(3):61    compute(i1)
  21. compute(i)

    60for i in range(3):61    compute(i2)
  22. info ← CacheInfo(hits=0, misses=8, maxsize=3, currsize=3)

    63info→ CacheInfo(hits=0, misses=8, maxsize=3, currsize=3) = compute⟨_lru_cache_wrapper B⟩.cache_info()64print(f"Hits: {info.hits0}")65print(f"Misses: {info.misses8}")66print(f"Size: {info.currsize3}")67print(f"Max size: {info.maxsize3}")6869# Cache clear70print("\nCache clear:")7172@lru_cache(maxsize=128)73def add(x, y):74    print(f"  Computing {x} + {y}")75    return x + y7677print("First calls:")78add(1, 2)79add(3, 4)
    outputHits: 0
    Misses: 8
    Size: 3
    Max size: 3
    
    Cache clear:
    First calls:
  23. def add(x, y):

    pass 1 of 3
    72@lru_cache(maxsize=128)73def add(x1, y2):74    print(f"  Computing {x1} + {y2}")75    return x1 + y2
    output  Computing 1 + 2
    All 3 passes — pass 1 is the card above
    passxy
    112
    234
    312
  24. add(1, 2)

    77print("First calls:")78add(1, 2)79add(3, 4)80add(1, 2)  # Cached
  25. add.cache_clear()

    78add(1, 2)79add(3, 4)80add(1, 2)  # Cached8182print("\nAfter clearing:")83add⟨_lru_cache_wrapper C⟩.cache_clear()84add(1, 2)  # Not cached anymore
    output
    After clearing:
  26. add(1, 2) # Not cached anymore

    83add.cache_clear()84add(1, 2)  # Not cached anymore8586# Maxsize effects87print("\nMaxsize effects:")8889@lru_cache(maxsize=2)90def limited(n):91    return n * n9293# Fill cache94limited(1)  # Miss95limited(2)  # Miss
    output
    Maxsize effects:
  27. def limited(n):

    pass 1 of 4
    89@lru_cache(maxsize=2)90def limited(n1):91    return n1 * n
    All 4 passes — pass 1 is the card above
    passn
    11
    22
    33
    42
  28. limited(1) # Miss

    93# Fill cache94limited(1)  # Miss95limited(2)  # Miss96limited(1)  # Hit
  29. limited(2) # Miss

    94limited(1)  # Miss95limited(2)  # Miss96limited(1)  # Hit9798# Evict oldest99limited(3)  # Miss, evicts 2100limited(1)  # Hit
  30. limited(3) # Miss, evicts 2

    98# Evict oldest99limited(3)  # Miss, evicts 2100limited(1)  # Hit101limited(2)  # Miss (was evicted)
  31. info ← CacheInfo(hits=2, misses=4, maxsize=2, currsize=2)

    100limited(1)  # Hit101limited(2)  # Miss (was evicted)102103info→ CacheInfo(hits=2, misses=4, maxsize=2, currsize=2) = limited⟨_lru_cache_wrapper D⟩.cache_info()104print(f"Hits: {info.hits2}, Misses: {info.misses4}")105106# Prime checking with cache107print("\nPrime checking with cache:")108109@lru_cache(maxsize=1000)110def is_prime(n):111    if n < 2:112        return False113    if n == 2:114        return True115    if n % 2 == 0:116        return False117    for i in range(3, int(n**0.5) + 1, 2):118        if n % i == 0:119            return False120    return True121122# Check primes multiple times123primes = [p for p in range(30) if is_prime(p)]124print(f"Primes < 30: {len(primes)}")
    outputHits: 2, Misses: 4
    
    Prime checking with cache:
  32. def is_prime(n):

    pass 1 of 30
    109@lru_cache(maxsize=1000)110def is_prime(n0):111    if n < 2:112        return False
    30 passes — pass 1 is the card above
    passn
    10
    21
    32
    43
    54
    65
    76
    87
    98
    ⋯ 19 more passes ⋯
    2928
    3029
  33. if n < 2:

    pass 1 of 2
    110def is_prime(n):111    if n0 < 2:112        return False113    if n == 2:
  34. if n < 2:

    pass 2 of 2
    110def is_prime(n):111    if n1 < 2:112        return False113    if n == 2:
  35. if n == 2:

    112    return False113if n2 == 2:114    return True115if n % 2 == 0:
  36. if n % 2 == 0:

    pass 1 of 13
    114    return True115if n4 % 2 == 0:116    return False117for i in range(3, int(n**0.5) + 1, 2):
    13 passes — pass 1 is the card above
    passn
    14
    26
    38
    410
    512
    614
    716
    818
    920
    ⋯ 2 more passes ⋯
    1226
    1328
  37. for i in range(3, int(n**0.5) + 1, 2):

    pass 1 of 13
    116    return False117for i3 in range(3, int(n9**0.5) + 1, 2):118    if n % i == 0:119        return False
    13 passes — pass 1 is the card above
    passin
    139
    2311
    3313
    4315
    5317
    6319
    7321
    8323
    9325
    ⋯ 2 more passes ⋯
    12329
    13529
  38. if n % i == 0:

    pass 1 of 5
    117for i in range(3, int(n**0.5) + 1, 2):118    if n9 % i3 == 0:119        return False120return True
    All 5 passes — pass 1 is the card above
    passni
    193
    2153
    3213
    4255
    5273
  39. return True

    119        return False120return True
  40. return True

    119        return False120return True
  41. return True

    119        return False120return True
  42. return True

    119        return False120return True
  43. return True

    119        return False120return True
  44. return True

    119        return False120return True
  45. primes ← [2, 3, 5, 7, 11, 13, 17, 19, 23, 29], primes_again ← [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

    122# Check primes multiple times123primes→ [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] = [p for p in range(30) if is_prime(p)]124print(f"Primes < 30: {len(primes[2, 3, 5, 7, 11, 13, 17, 19, 23, 29])}")125126# Recheck (cached)127primes_again→ [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] = [p for p in range(30) if is_prime(p)]128print(f"Rechecking returns same values: {primes_again[2, 3, 5, 7, 11, 13, 17, 19, 23, 29] == primes[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]}")129130info→ CacheInfo(hits=30, misses=30, maxsize=1000, currsize=30) = is_prime⟨_lru_cache_wrapper E⟩.cache_info()131print(f"Cache: {info.hits30} hits, {info.misses30} misses")132133# Distance calculation134print("\nDistance calculation:")135136@lru_cache(maxsize=256)137def distance(x1, y1, x2, y2):138    """Calculate Euclidean distance."""139    return ((x2 - x1)**2 + (y2 - y1)**2)**0.5140141# Calculate distances142points→ [(0, 0), (3, 4), (1, 1), (0, 0), (3, 4)] = [(0, 0), (3, 4), (1, 1), (0, 0), (3, 4)]
    outputPrimes < 30: 10
    Rechecking returns same values: True
    Cache: 30 hits, 30 misses
    
    Distance calculation:
  46. for i in range(len(points)):

    pass 1 of 5
    144for i0 in range(len(points[(0, 0), (3, 4), (1, 1), (0, 0), (3, 4)])):145    for j in range(i + 1, len(points)):146        d = distance(*points[i], *points[j])
    All 5 passes — pass 1 is the card above
    passi
    10
    21
    32
    43
    54
  47. for j in range(i + 1, len(points)):

    pass 1 of 10
    144for i in range(len(points)):145    for j1 in range(i0 + 1, len(points[(0, 0), (3, 4), (1, 1), (0, 0), (3, 4)])):146        d = distance(*points[i](0, 0), *points[j](3, 4))147        print(f"  {points[i]} to {points[j]}: {d:.2f}")
    All 10 passes — pass 1 is the card above
    passjipoints[i]points[j]d
    110(0, 0)(3, 4)
    220(0, 0)(1, 1)
    330(0, 0)(0, 0)
    440(0, 0)(3, 4)5.0
    521(3, 4)(1, 1)
    631(3, 4)(0, 0)
    741(3, 4)(3, 4)
    832(1, 1)(0, 0)
    942(1, 1)(3, 4)
    1043(0, 0)(3, 4)5.0
  48. def distance(x1, y1, x2, y2):

    pass 1 of 8
    136@lru_cache(maxsize=256)137def distance(x10, y10, x23, y24):138    """Calculate Euclidean distance."""139    return ((x23 - x10)**2 + (y24 - y10)**2)**0.5
    All 8 passes — pass 1 is the card above
    passx1y1x2y2
    10034
    20011
    30000
    43411
    53400
    63434
    71100
    81134
  49. d ← 5.0

    145for j in range(i + 1, len(points)):146    d→ 5.0 = distance(*points[i](0, 0), *points[j](3, 4))147    print(f"  {points[i](0, 0)} to {points[j](3, 4)}: {d5.0:.2f}")
    output  (0, 0) to (3, 4): 5.00
  50. d ← 1.4142135623730951

    145for j in range(i + 1, len(points)):146    d→ 1.4142135623730951 = distance(*points[i](0, 0), *points[j](1, 1))147    print(f"  {points[i](0, 0)} to {points[j](1, 1)}: {d1.4142135623730951:.2f}")
    output  (0, 0) to (1, 1): 1.41
  51. d ← 0.0

    145for j in range(i + 1, len(points)):146    d→ 0.0 = distance(*points[i](0, 0), *points[j](0, 0))147    print(f"  {points[i](0, 0)} to {points[j](0, 0)}: {d0.0:.2f}")
    output  (0, 0) to (0, 0): 0.00
  52. d ← 3.605551275463989

    145for j in range(i + 1, len(points)):146    d→ 3.605551275463989 = distance(*points[i](3, 4), *points[j](1, 1))147    print(f"  {points[i](3, 4)} to {points[j](1, 1)}: {d3.605551275463989:.2f}")
    output  (3, 4) to (1, 1): 3.61
  53. d ← 5.0

    145for j in range(i + 1, len(points)):146    d→ 5.0 = distance(*points[i](3, 4), *points[j](0, 0))147    print(f"  {points[i](3, 4)} to {points[j](0, 0)}: {d5.0:.2f}")
    output  (3, 4) to (0, 0): 5.00
  54. d ← 0.0

    145for j in range(i + 1, len(points)):146    d→ 0.0 = distance(*points[i](3, 4), *points[j](3, 4))147    print(f"  {points[i](3, 4)} to {points[j](3, 4)}: {d0.0:.2f}")
    output  (3, 4) to (3, 4): 0.00
  55. d ← 1.4142135623730951

    145for j in range(i + 1, len(points)):146    d→ 1.4142135623730951 = distance(*points[i](1, 1), *points[j](0, 0))147    print(f"  {points[i](1, 1)} to {points[j](0, 0)}: {d1.4142135623730951:.2f}")
    output  (1, 1) to (0, 0): 1.41
  56. d ← 3.605551275463989

    145for j in range(i + 1, len(points)):146    d→ 3.605551275463989 = distance(*points[i](1, 1), *points[j](3, 4))147    print(f"  {points[i](1, 1)} to {points[j](3, 4)}: {d3.605551275463989:.2f}")
    output  (1, 1) to (3, 4): 3.61
  57. info ← CacheInfo(hits=2, misses=8, maxsize=256, currsize=8)

    149info→ CacheInfo(hits=2, misses=8, maxsize=256, currsize=8) = distance⟨_lru_cache_wrapper F⟩.cache_info()150print(f"Cache: {info.hits2} hits, {info.misses8} misses")151152# Memoization pattern153print("\nMemoization pattern:")154155@lru_cache(maxsize=None)156def count_paths(m, n):157    """Count paths in m x n grid."""158    if m == 1 or n == 1:159        return 1160    return count_paths(m - 1, n) + count_paths(m, n - 1)161162result = count_paths(5, 5)163print(f"Paths in 5x5 grid: {result}")
    outputCache: 2 hits, 8 misses
    
    Memoization pattern:
  58. def count_paths(m, n):

    pass 1 of 24
    155@lru_cache(maxsize=None)156def count_paths(m5, n5):157    """Count paths in m x n grid."""158    if m == 1 or n == 1:159        return 1160    return count_paths(m5 - 1, n5) + count_paths(m, n - 1)
    24 passes — pass 1 is the card above
    passmn
    155
    245
    335
    425
    515
    624
    714
    823
    913
    ⋯ 13 more passes ⋯
    2352
    2451
  59. if m == 1 or n == 1:

    pass 1 of 8
    157"""Count paths in m x n grid."""158if m1 == 1 or n5 == 1:159    return 1160return count_paths(m - 1, n) + count_paths(m, n - 1)
    All 8 passes — pass 1 is the card above
    passmn
    115
    214
    313
    412
    521
    631
    741
    851
  60. result ← 70, info ← CacheInfo(hits=9, misses=24, maxsize=None, currsize=24)

    162result→ 70 = count_paths(5, 5)163print(f"Paths in 5x5 grid: {result70}")164165info→ CacheInfo(hits=9, misses=24, maxsize=None, currsize=24) = count_paths⟨_lru_cache_wrapper G⟩.cache_info()166print(f"Cache efficiency: {info.hits9}/{info.hits + info.misses24} hits")
    outputPaths in 5x5 grid: 70
    Cache efficiency: 9/33 hits
lru_cache Least Recently Used cache decorator that stores recent function results, dramatically speeding up recursive algorithms and repeated calculations.

Partial Function Application

Create specialized versions of functions:

partial.py
Replay: real traced execution (multi-file project)
# functools.partial examples

from functools import partial

# Basic partial
print("Basic partial:")

def multiply(x, y):
    return x * y

# Create specialized functions
double = partial(multiply, 2)
triple = partial(multiply, 3)

print(f"double(5) = {double(5)}")
print(f"triple(5) = {triple(5)}")

# Partial with multiple args
print("\nPartial with multiple args:")

def power(base, exponent):
    return base ** exponent

# Fix base
square = partial(power, exponent=2)
cube = partial(power, exponent=3)

print(f"square(5) = {square(5)}")
print(f"cube(5) = {cube(5)}")

# Fix exponent
power_of_2 = partial(power, 2)
power_of_10 = partial(power, 10)

print(f"2^8 = {power_of_2(8)}")
print(f"10^3 = {power_of_10(3)}")

# String formatting
print("\nString formatting:")

def format_message(prefix, message, suffix):
    return f"{prefix}{message}{suffix}"

# Create specialized formatters
error = partial(format_message, "[ERROR] ", suffix="")
warning = partial(format_message, "[WARN] ", suffix="")
info = partial(format_message, "[INFO] ", suffix="")

print(error("File not found"))
print(warning("Deprecated function"))
print(info("Process started"))

# Sorting with partial
print("\nSorting with partial:")

# Sort by custom key
students = [
    {'name': 'Alice', 'age': 20, 'grade': 85},
    {'name': 'Bob', 'age': 22, 'grade': 92},
    {'name': 'Charlie', 'age': 21, 'grade': 78}
]

from operator import itemgetter

# Create reusable key functions
by_name = partial(sorted, key=itemgetter('name'))
by_age = partial(sorted, key=itemgetter('age'))
by_grade = partial(sorted, key=itemgetter('grade'))

print("By name:")
for s in by_name(students):
    print(f"  {s['name']}: {s['grade']}")

print("By grade:")
for s in by_grade(students):
    print(f"  {s['name']}: {s['grade']}")

# File operations
print("\nFile operations:")

def read_file(filename, mode='r', encoding='utf-8'):
    """Simulate file reading."""
    return f"Reading {filename} (mode={mode}, encoding={encoding})"

# Create specialized readers
read_text = partial(read_file, mode='r', encoding='utf-8')
read_binary = partial(read_file, mode='rb', encoding=None)
read_json = partial(read_file, mode='r', encoding='utf-8')

print(read_text('data.txt'))
print(read_binary('image.png'))

# Logging
print("\nLogging:")

def log(level, component, message):
    print(f"[{level}] {component}: {message}")

# Create component-specific loggers
db_log = partial(log, component='Database')
api_log = partial(log, component='API')
cache_log = partial(log, component='Cache')

db_log(level='INFO', message='Connected to PostgreSQL')
api_log(level='WARN', message='Rate limit approaching')
cache_log(level='ERROR', message='Redis connection failed')

# Map with partial
print("\nMap with partial:")

def add(x, y):
    return x + y

# Add 10 to each number
numbers = [1, 2, 3, 4, 5]
add_10 = partial(add, 10)
result = list(map(add_10, numbers))

print(f"Original: {numbers}")
print(f"Add 10:   {result}")

# Filter with partial
print("\nFilter with partial:")

def greater_than(threshold, value):
    return value > threshold

# Filter values > 50
values = [25, 60, 45, 80, 30, 95]
above_50 = partial(greater_than, 50)
filtered = list(filter(above_50, values))

print(f"Values: {values}")
print(f"Above 50: {filtered}")

# Callback functions
print("\nCallback functions:")

def process_data(data, validator, transformer):
    if validator(data):
        return transformer(data)
    return None

def is_positive(x):
    return x > 0

def is_even(x):
    return x % 2 == 0

def square(x):
    return x * x

# Create specialized processors
process_positive = partial(process_data, validator=is_positive, transformer=square)
process_even = partial(process_data, validator=is_even, transformer=square)

print(f"Process positive 5: {process_positive(5)}")
print(f"Process positive -3: {process_positive(-3)}")
print(f"Process even 4: {process_even(4)}")
print(f"Process even 3: {process_even(3)}")

# Partial attributes
print("\nPartial attributes:")

greet = partial(format_message, "Hello, ")

print(f"Function: {greet.func}")
print(f"Args: {greet.args}")
print(f"Keywords: {greet.keywords}")

  1. double ← functools.partial(⟨function multiply A⟩, 2), triple ← functools.partial(⟨function multiply A⟩, 3)

    5# Basic partial6print("Basic partial:")78def multiply(x, y):9    return x * y1011# Create specialized functions12double→ functools.partial(⟨function multiply A⟩, 2) = partial(multiply⟨function multiply A⟩, 2)13triple→ functools.partial(⟨function multiply A⟩, 3) = partial(multiply⟨function multiply A⟩, 3)1415print(f"double(5) = {double(5)}")16print(f"triple(5) = {triple(5)}")
    outputBasic partial:
  2. def multiply(x, y):

    pass 1 of 2
    8def multiply(x2, y5):9    return x2 * y5
  3. print(f"double(5) = {double(5)}")

    15print(f"double(5) = {double(5)}")16print(f"triple(5) = {triple(5)}")
    outputdouble(5) = 10
  4. def multiply(x, y):

    pass 2 of 2
    8def multiply(x3, y5):9    return x3 * y5
  5. square ← functools.partial(⟨function power B⟩, exponent=2), cube ← functools.partial(⟨function power B⟩, exponent=3)

    15print(f"double(5) = {double(5)}")16print(f"triple(5) = {triple(5)}")1718# Partial with multiple args19print("\nPartial with multiple args:")2021def power(base, exponent):22    return base ** exponent2324# Fix base25square→ functools.partial(⟨function power B⟩, exponent=2) = partial(power⟨function power B⟩, exponent=2)26cube→ functools.partial(⟨function power B⟩, exponent=3) = partial(power⟨function power B⟩, exponent=3)2728print(f"square(5) = {square(5)}")29print(f"cube(5) = {cube(5)}")
    outputtriple(5) = 15
    
    Partial with multiple args:
  6. def power(base, exponent):

    pass 1 of 4
    21def power(base5, exponent2):22    return base5 ** exponent2
    All 4 passes — pass 1 is the card above
    passbaseexponent
    152
    253
    328
    4103
  7. print(f"square(5) = {square(5)}")

    28print(f"square(5) = {square(5)}")29print(f"cube(5) = {cube(5)}")
    outputsquare(5) = 25
  8. power_of_2 ← functools.partial(⟨function power B⟩, 2), power_of_10 ← functools.partial(⟨function power B⟩, 10)

    28print(f"square(5) = {square(5)}")29print(f"cube(5) = {cube(5)}")3031# Fix exponent32power_of_2→ functools.partial(⟨function power B⟩, 2) = partial(power⟨function power B⟩, 2)33power_of_10→ functools.partial(⟨function power B⟩, 10) = partial(power⟨function power B⟩, 10)3435print(f"2^8 = {power_of_2(8)}")36print(f"10^3 = {power_of_10(3)}")
    outputcube(5) = 125
  9. print(f"2^8 = {power_of_2(8)}")

    35print(f"2^8 = {power_of_2(8)}")36print(f"10^3 = {power_of_10(3)}")
    output2^8 = 256
  10. error ← functools.partial(⟨function format_message C⟩, '[ERROR] ', suffix='')

    35print(f"2^8 = {power_of_2(8)}")36print(f"10^3 = {power_of_10(3)}")3738# String formatting39print("\nString formatting:")4041def format_message(prefix, message, suffix):42    return f"{prefix}{message}{suffix}"4344# Create specialized formatters45error→ functools.partial(⟨function format_message C⟩, '[ERROR] ', suffix='') = partial(format_message⟨function format_message C⟩, "[ERROR] ", suffix="")46warning→ functools.partial(⟨function format_message C⟩, '[WARN] ', suffix='') = partial(format_message⟨function format_message C⟩, "[WARN] ", suffix="")47info→ functools.partial(⟨function format_message C⟩, '[INFO] ', suffix='') = partial(format_message⟨function format_message C⟩, "[INFO] ", suffix="")4849print(error("File not found"))50print(warning("Deprecated function"))
    output10^3 = 1000
    
    String formatting:
  11. def format_message(prefix, message, suffix):

    pass 1 of 3
    41def format_message(prefix[ERROR] , messageFile not found, suffix(empty)):42    return f"{prefix[ERROR] }{messageFile not found}{suffix(empty)}"
    All 3 passes — pass 1 is the card above
    passprefixmessage
    1[ERROR] File not found
    2[WARN] Deprecated function
    3[INFO] Process started
  12. print(error("File not found"))

    49print(error("File not found"))50print(warning("Deprecated function"))51print(info("Process started"))
    output[ERROR] File not found
  13. print(warning("Deprecated function"))

    49print(error("File not found"))50print(warning("Deprecated function"))51print(info("Process started"))
    output[WARN] Deprecated function
  14. students ← [{'name': 'Alice', 'age': 20, 'grade': 85}, {'name': 'Bob', 'age': 22, 'grade': 92}, {'name': 'Charlie', 'age': 21, 'grade': 78}]

    50print(warning("Deprecated function"))51print(info("Process started"))5253# Sorting with partial54print("\nSorting with partial:")5556# Sort by custom key57students→ [{'name': 'Alice', 'age': 20, 'grade': 85}, {'name': 'Bob', 'age': 22, 'grade': 92}, {'name': 'Charlie', 'age': 21, 'grade': 78}] = [58    {'name': 'Alice', 'age': 20, 'grade': 85},59    {'name': 'Bob', 'age': 22, 'grade': 92},60    {'name': 'Charlie', 'age': 21, 'grade': 78}61]6263from operator import itemgetter6465# Create reusable key functions66by_name→ functools.partial(<built-in function sorted>, key=operator.itemgetter('name')) = partial(sorted, key=itemgetter('name'))67by_age→ functools.partial(<built-in function sorted>, key=operator.itemgetter('age')) = partial(sorted, key=itemgetter('age'))68by_grade→ functools.partial(<built-in function sorted>, key=operator.itemgetter('grade')) = partial(sorted, key=itemgetter('grade'))6970print("By name:")71for s in by_name(students):
    output[INFO] Process started
    
    Sorting with partial:
    By name:
  15. for s in by_name(students):

    pass 1 of 3
    70print("By name:")71for s{'name': 'Alice', 'age': 20, 'grade': 85} in by_name(students[{'name': 'Alice', 'age': 20, 'grade': 85}, {'name': 'Bob', 'age': 22, 'grade': 92}, {'name': 'Charlie', 'age': 21, 'grade': 78}]):72    print(f"  {s['name']Alice}: {s['grade']85}")
    output  Alice: 85
    All 3 passes — pass 1 is the card above
    passss[’name’]s[’grade’]
    1{'name': 'Alice', 'age': 20, 'grade': 85}Alice85
    2{'name': 'Bob', 'age': 22, 'grade': 92}Bob92
    3{'name': 'Charlie', 'age': 21, 'grade': 78}Charlie78
  16. print("By grade:")

    74print("By grade:")75for s in by_grade(students):
    outputBy grade:
  17. for s in by_grade(students):

    pass 1 of 3
    74print("By grade:")75for s{'name': 'Charlie', 'age': 21, 'grade': 78} in by_grade(students[{'name': 'Alice', 'age': 20, 'grade': 85}, {'name': 'Bob', 'age': 22, 'grade': 92}, {'name': 'Charlie', 'age': 21, 'grade': 78}]):76    print(f"  {s['name']Charlie}: {s['grade']78}")
    output  Charlie: 78
    All 3 passes — pass 1 is the card above
    passss[’name’]s[’grade’]
    1{'name': 'Charlie', 'age': 21, 'grade': 78}Charlie78
    2{'name': 'Alice', 'age': 20, 'grade': 85}Alice85
    3{'name': 'Bob', 'age': 22, 'grade': 92}Bob92
  18. read_text ← functools.partial(⟨function read_file D⟩, mode='r', encoding='utf-8')

    78# File operations79print("\nFile operations:")8081def read_file(filename, mode='r', encoding='utf-8'):82    """Simulate file reading."""83    return f"Reading {filename} (mode={mode}, encoding={encoding})"8485# Create specialized readers86read_text→ functools.partial(⟨function read_file D⟩, mode='r', encoding='utf-8') = partial(read_file⟨function read_file D⟩, mode='r', encoding='utf-8')87read_binary→ functools.partial(⟨function read_file D⟩, mode='rb', encoding=None) = partial(read_file⟨function read_file D⟩, mode='rb', encoding=None)88read_json→ functools.partial(⟨function read_file D⟩, mode='r', encoding='utf-8') = partial(read_file⟨function read_file D⟩, mode='r', encoding='utf-8')8990print(read_text('data.txt'))91print(read_binary('image.png'))
    output
    File operations:
  19. def read_file(filename, mode='r', encoding='utf-8'):

    pass 1 of 2
    81def read_file(filenamedata.txt, moder='r', encodingutf-8='utf-8'):82    """Simulate file reading."""83    return f"Reading {filenamedata.txt} (mode={moder}, encoding={encodingutf-8})"
  20. print(read_text('data.txt'))

    90print(read_text('data.txt'))91print(read_binary('image.png'))
    outputReading data.txt (mode=r, encoding=utf-8)
  21. def read_file(filename, mode='r', encoding='utf-8'):

    pass 2 of 2
    81def read_file(filenameimage.png, moderb='r', encodingNone='utf-8'):82    """Simulate file reading."""83    return f"Reading {filenameimage.png} (mode={moderb}, encoding={encodingNone})"
  22. db_log ← functools.partial(⟨function log E⟩, component='Database')

    90print(read_text('data.txt'))91print(read_binary('image.png'))9293# Logging94print("\nLogging:")9596def log(level, component, message):97    print(f"[{level}] {component}: {message}")9899# Create component-specific loggers100db_log→ functools.partial(⟨function log E⟩, component='Database') = partial(log⟨function log E⟩, component='Database')101api_log→ functools.partial(⟨function log E⟩, component='API') = partial(log⟨function log E⟩, component='API')102cache_log→ functools.partial(⟨function log E⟩, component='Cache') = partial(log⟨function log E⟩, component='Cache')103104db_log(level='INFO', message='Connected to PostgreSQL')105api_log(level='WARN', message='Rate limit approaching')
    outputReading image.png (mode=rb, encoding=None)
    
    Logging:
  23. def log(level, component, message):

    pass 1 of 3
    96def log(levelINFO, componentDatabase, messageConnected to PostgreSQL):97    print(f"[{levelINFO}] {componentDatabase}: {messageConnected to PostgreSQL}")
    output[INFO] Database: Connected to PostgreSQL
    All 3 passes — pass 1 is the card above
    passlevelcomponentmessage
    1INFODatabaseConnected to PostgreSQL
    2WARNAPIRate limit approaching
    3ERRORCacheRedis connection failed
  24. db_log(level='INFO', message='Connected to PostgreSQL')

    104db_log(level='INFO', message='Connected to PostgreSQL')105api_log(level='WARN', message='Rate limit approaching')106cache_log(level='ERROR', message='Redis connection failed')
  25. api_log(level='WARN', message='Rate limit approaching')

    104db_log(level='INFO', message='Connected to PostgreSQL')105api_log(level='WARN', message='Rate limit approaching')106cache_log(level='ERROR', message='Redis connection failed')
  26. numbers ← [1, 2, 3, 4, 5], add_10 ← functools.partial(⟨function add F⟩, 10)

    105api_log(level='WARN', message='Rate limit approaching')106cache_log(level='ERROR', message='Redis connection failed')107108# Map with partial109print("\nMap with partial:")110111def add(x, y):112    return x + y113114# Add 10 to each number115numbers→ [1, 2, 3, 4, 5] = [1, 2, 3, 4, 5]116add_10→ functools.partial(⟨function add F⟩, 10) = partial(add⟨function add F⟩, 10)117result = list(map(add_10functools.partial(⟨function add F⟩, 10), numbers[1, 2, 3, 4, 5]))
    output
    Map with partial:
  27. def add(x, y):

    pass 1 of 5
    111def add(x10, y1):112    return x10 + y1
    All 5 passes — pass 1 is the card above
    passy
    11
    22
    33
    44
    55
  28. result ← [11, 12, 13, 14, 15], values ← [25, 60, 45, 80, 30, 95]

    116add_10 = partial(add, 10)117result→ [11, 12, 13, 14, 15] = list(map(add_10functools.partial(⟨function add F⟩, 10), numbers[1, 2, 3, 4, 5]))118119print(f"Original: {numbers[1, 2, 3, 4, 5]}")120print(f"Add 10:   {result[11, 12, 13, 14, 15]}")121122# Filter with partial123print("\nFilter with partial:")124125def greater_than(threshold, value):126    return value > threshold127128# Filter values > 50129values→ [25, 60, 45, 80, 30, 95] = [25, 60, 45, 80, 30, 95]130above_50→ functools.partial(⟨function greater_than G⟩, 50) = partial(greater_than⟨function greater_than G⟩, 50)131filtered = list(filter(above_50functools.partial(⟨function greater_than G⟩, 50), values[25, 60, 45, 80, 30, 95]))
    outputOriginal: [1, 2, 3, 4, 5]
    Add 10:   [11, 12, 13, 14, 15]
    
    Filter with partial:
  29. def greater_than(threshold, value):

    pass 1 of 6
    125def greater_than(threshold50, value25):126    return value25 > threshold50
    All 6 passes — pass 1 is the card above
    passvalue
    125
    260
    345
    480
    530
    695
  30. filtered ← [60, 80, 95], process_positive ← functools.partial(⟨function process_data H⟩, validator=⟨function is_positive I⟩, transformer=⟨function square J⟩)

    130above_50 = partial(greater_than, 50)131filtered→ [60, 80, 95] = list(filter(above_50functools.partial(⟨function greater_than G⟩, 50), values[25, 60, 45, 80, 30, 95]))132133print(f"Values: {values[25, 60, 45, 80, 30, 95]}")134print(f"Above 50: {filtered[60, 80, 95]}")135136# Callback functions137print("\nCallback functions:")138139def process_data(data, validator, transformer):140    if validator(data):141        return transformer(data)142    return None143144def is_positive(x):145    return x > 0146147def is_even(x):148    return x % 2 == 0149150def square(x):151    return x * x152153# Create specialized processors154process_positive→ functools.partial(⟨function process_data H⟩, validator=⟨function is_positive I⟩, transformer=⟨function square J⟩) = partial(process_data⟨function process_data H⟩, validator=is_positive⟨function is_positive I⟩, transformer=square⟨function square J⟩)155process_even→ functools.partial(⟨function process_data H⟩, validator=⟨function is_even K⟩, transformer=⟨function square J⟩) = partial(process_data⟨function process_data H⟩, validator=is_even⟨function is_even K⟩, transformer=square⟨function square J⟩)156157print(f"Process positive 5: {process_positive(5)}")158print(f"Process positive -3: {process_positive(-3)}")
    outputValues: [25, 60, 45, 80, 30, 95]
    Above 50: [60, 80, 95]
    
    Callback functions:
  31. def process_data(data, validator, transformer):

    pass 1 of 4
    139def process_data(data5, validator⟨function is_positive I⟩, transformer⟨function square J⟩):140    if validator(data):141        return transformer(data)
    All 4 passes — pass 1 is the card above
    passdatavalidatorx
    15⟨function is_positive I⟩5
    2-3⟨function is_positive I⟩-3
    34⟨function is_even K⟩4
    43⟨function is_even K⟩3
  32. def is_positive(x):

    pass 1 of 2
    144def is_positive(x5):145    return x5 > 0
  33. if validator(data):

    pass 1 of 2
    139def process_data(data, validator, transformer):140    if validator(data5):141        return transformer(data5)142    return None
  34. def square(x):

    pass 1 of 2
    150def square(x5):151    return x5 * x
  35. print(f"Process positive 5: {process_positive(5)}")

    157print(f"Process positive 5: {process_positive(5)}")158print(f"Process positive -3: {process_positive(-3)}")159print(f"Process even 4: {process_even(4)}")
    outputProcess positive 5: 25
  36. def is_positive(x):

    pass 2 of 2
    144def is_positive(x-3):145    return x-3 > 0
  37. return None

    141    return transformer(data)142return None
  38. print(f"Process positive -3: {process_positive(-3)}")

    157print(f"Process positive 5: {process_positive(5)}")158print(f"Process positive -3: {process_positive(-3)}")159print(f"Process even 4: {process_even(4)}")160print(f"Process even 3: {process_even(3)}")
    outputProcess positive -3: None
  39. def is_even(x):

    pass 1 of 2
    147def is_even(x4):148    return x4 % 2 == 0
  40. if validator(data):

    pass 2 of 2
    139def process_data(data, validator, transformer):140    if validator(data4):141        return transformer(data4)142    return None
  41. def square(x):

    pass 2 of 2
    150def square(x4):151    return x4 * x
  42. print(f"Process even 4: {process_even(4)}")

    158print(f"Process positive -3: {process_positive(-3)}")159print(f"Process even 4: {process_even(4)}")160print(f"Process even 3: {process_even(3)}")
    outputProcess even 4: 16
  43. def is_even(x):

    pass 2 of 2
    147def is_even(x3):148    return x3 % 2 == 0
  44. return None

    141    return transformer(data)142return None
  45. greet ← functools.partial(⟨function format_message C⟩, 'Hello, ')

    159print(f"Process even 4: {process_even(4)}")160print(f"Process even 3: {process_even(3)}")161162# Partial attributes163print("\nPartial attributes:")164165greet→ functools.partial(⟨function format_message C⟩, 'Hello, ') = partial(format_message⟨function format_message C⟩, "Hello, ")166167print(f"Function: {greet.func⟨function format_message C⟩}")168print(f"Args: {greet.args('Hello, ',)}")169print(f"Keywords: {greet.keywords{}}")
    outputProcess even 3: None
    
    Partial attributes:
    Function: ⟨function format_message C⟩
    Args: ('Hello, ',)
    Keywords: {}
partial() Creates a new function with some arguments pre-filled, reducing repetition and creating more specific functions from general ones.

Single Dispatch

Function overloading based on argument type:

dispatch.py
Replay: real traced execution (multi-file project)
# functools.singledispatch examples

from functools import singledispatch
from decimal import Decimal

# Basic singledispatch
print("Basic singledispatch:")

@singledispatch
def process(arg):
    """Default implementation."""
    print(f"Processing {type(arg).__name__}: {arg}")

@process.register(int)
def _(arg):
    print(f"Integer: {arg} (squared: {arg**2})")

@process.register(str)
def _(arg):
    print(f"String: '{arg}' (length: {len(arg)})")

@process.register(list)
def _(arg):
    print(f"List: {arg} (sum: {sum(arg)})")

# Call with different types
process(10)
process("hello")
process([1, 2, 3, 4, 5])
process(3.14)  # Uses default

# Format function
print("\nFormat function:")

@singledispatch
def format_value(val):
    """Default formatter."""
    return str(val)

@format_value.register(int)
def _(val):
    return f"{val:,}"

@format_value.register(float)
def _(val):
    return f"{val:.2f}"

@format_value.register(bool)
def _(val):
    return "Yes" if val else "No"

@format_value.register(list)
def _(val):
    return f"[{len(val)} items]"

print(f"Integer: {format_value(1000000)}")
print(f"Float: {format_value(3.14159)}")
print(f"Bool: {format_value(True)}")
print(f"List: {format_value([1, 2, 3, 4, 5])}")

# Serialize function
print("\nSerialize function:")

@singledispatch
def serialize(obj):
    """Default serialization."""
    raise NotImplementedError(f"Cannot serialize {type(obj)}")

@serialize.register(int)
@serialize.register(float)
@serialize.register(str)
def _(obj):
    return obj

@serialize.register(list)
def _(obj):
    return [serialize(item) for item in obj]

@serialize.register(dict)
def _(obj):
    return {key: serialize(value) for key, value in obj.items()}

data = {
    'name': 'Alice',
    'age': 30,
    'scores': [85, 92, 78],
    'active': True
}

try:
    result = serialize(data)
    print(f"Serialized: {result}")
except NotImplementedError as e:
    print(f"Error: {e}")

# Area calculation
print("\nArea calculation:")

@singledispatch
def area(shape):
    """Calculate area of shape."""
    raise NotImplementedError(f"Unknown shape: {type(shape)}")

class Circle:
    def __init__(self, radius):
        self.radius = radius

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

class Triangle:
    def __init__(self, base, height):
        self.base = base
        self.height = height

@area.register(Circle)
def _(shape):
    import math
    return math.pi * shape.radius ** 2

@area.register(Rectangle)
def _(shape):
    return shape.width * shape.height

@area.register(Triangle)
def _(shape):
    return 0.5 * shape.base * shape.height

# Calculate areas
circle = Circle(5)
rectangle = Rectangle(4, 6)
triangle = Triangle(3, 8)

print(f"Circle area: {area(circle):.2f}")
print(f"Rectangle area: {area(rectangle):.2f}")
print(f"Triangle area: {area(triangle):.2f}")

# Type conversion
print("\nType conversion:")

@singledispatch
def to_string(val):
    """Convert to string."""
    return str(val)

@to_string.register(int)
def _(val):
    return f"Integer: {val}"

@to_string.register(float)
def _(val):
    return f"Float: {val:.4f}"

@to_string.register(Decimal)
def _(val):
    return f"Decimal: {val}"

@to_string.register(list)
def _(val):
    return f"List with {len(val)} elements"

print(to_string(42))
print(to_string(3.14159))
print(to_string(Decimal('10.50')))
print(to_string([1, 2, 3]))

# HTML rendering
print("\nHTML rendering:")

@singledispatch
def to_html(data):
    """Render data as HTML."""
    return f"<span>{data}</span>"

@to_html.register(int)
@to_html.register(float)
def _(data):
    return f"<strong>{data}</strong>"

@to_html.register(str)
def _(data):
    return f"<p>{data}</p>"

@to_html.register(list)
def _(data):
    items = ''.join(f"<li>{to_html(item)}</li>" for item in data)
    return f"<ul>{items}</ul>"

@to_html.register(dict)
def _(data):
    rows = ''.join(
        f"<tr><td>{key}</td><td>{to_html(value)}</td></tr>"
        for key, value in data.items()
    )
    return f"<table>{rows}</table>"

print(to_html("Hello"))
print(to_html(42))
print(to_html([1, 2, 3]))

# Validator
print("\nValidator:")

@singledispatch
def validate(val):
    """Validate value."""
    return True

@validate.register(str)
def _(val):
    return len(val) > 0

@validate.register(int)
def _(val):
    return val >= 0

@validate.register(list)
def _(val):
    return len(val) > 0 and all(validate(item) for item in val)

print(f"Validate 'hello': {validate('hello')}")
print(f"Validate '': {validate('')}")
print(f"Validate 10: {validate(10)}")
print(f"Validate -5: {validate(-5)}")
print(f"Validate [1, 2]: {validate([1, 2])}")

# Type dispatch info
print("\nType dispatch info:")

@singledispatch
def demo(arg):
    return "default"

@demo.register(int)
def _(arg):
    return "int"

@demo.register(str)
def _(arg):
    return "str"

# Check registered types
print(f"Registry: {demo.registry.keys()}")
print(f"Dispatch(int): {demo.dispatch(int)}")
print(f"Dispatch(str): {demo.dispatch(str)}")

  1. print("Basic singledispatch:")

    6# Basic singledispatch7print("Basic singledispatch:")89@singledispatch10def process(arg):11    """Default implementation."""12    print(f"Processing {type(arg).__name__}: {arg}")1314@process.register(int)15def _(arg):16    print(f"Integer: {arg} (squared: {arg**2})")1718@process.register(str)19def _(arg):20    print(f"String: '{arg}' (length: {len(arg)})")2122@process.register(list)23def _(arg):24    print(f"List: {arg} (sum: {sum(arg)})")2526# Call with different types27process(10)28process("hello")
    outputBasic singledispatch:
  2. def _(arg):

    14@process.register(int)15def _(arg10):16    print(f"Integer: {arg10} (squared: {arg**2})")
    outputInteger: 10 (squared: 100)
  3. process(10)

    26# Call with different types27process(10)28process("hello")29process([1, 2, 3, 4, 5])
  4. def _(arg):

    18@process.register(str)19def _(arghello):20    print(f"String: '{arghello}' (length: {len(arg)})")
    outputString: 'hello' (length: 5)
  5. process("hello")

    27process(10)28process("hello")29process([1, 2, 3, 4, 5])30process(3.14)  # Uses default
  6. def _(arg):

    22@process.register(list)23def _(arg[1, 2, 3, 4, 5]):24    print(f"List: {arg[1, 2, 3, 4, 5]} (sum: {sum(arg)})")
    outputList: [1, 2, 3, 4, 5] (sum: 15)
  7. process([1, 2, 3, 4, 5])

    28process("hello")29process([1, 2, 3, 4, 5])30process(3.14)  # Uses default
  8. def process(arg):

    9@singledispatch10def process(arg3.14):11    """Default implementation."""12    print(f"Processing {type(arg3.14).__name__}: {arg}")
    outputProcessing float: 3.14
  9. process(3.14) # Uses default

    29process([1, 2, 3, 4, 5])30process(3.14)  # Uses default3132# Format function33print("\nFormat function:")3435@singledispatch36def format_value(val):37    """Default formatter."""38    return str(val)3940@format_value.register(int)41def _(val):42    return f"{val:,}"4344@format_value.register(float)45def _(val):46    return f"{val:.2f}"4748@format_value.register(bool)49def _(val):50    return "Yes" if val else "No"5152@format_value.register(list)53def _(val):54    return f"[{len(val)} items]"5556print(f"Integer: {format_value(1000000)}")57print(f"Float: {format_value(3.14159)}")
    output
    Format function:
  10. def _(val):

    40@format_value.register(int)41def _(val1000000):42    return f"{val1000000:,}"
  11. print(f"Integer: {format_value(1000000)}")

    56print(f"Integer: {format_value(1000000)}")57print(f"Float: {format_value(3.14159)}")58print(f"Bool: {format_value(True)}")
    outputInteger: 1,000,000
  12. def _(val):

    44@format_value.register(float)45def _(val3.14159):46    return f"{val3.14159:.2f}"
  13. print(f"Float: {format_value(3.14159)}")

    56print(f"Integer: {format_value(1000000)}")57print(f"Float: {format_value(3.14159)}")58print(f"Bool: {format_value(True)}")59print(f"List: {format_value([1, 2, 3, 4, 5])}")
    outputFloat: 3.14
  14. def _(val):

    48@format_value.register(bool)49def _(valTrue):50    return "Yes" if valTrue else "No"
  15. print(f"Bool: {format_value(True)}")

    57print(f"Float: {format_value(3.14159)}")58print(f"Bool: {format_value(True)}")59print(f"List: {format_value([1, 2, 3, 4, 5])}")
    outputBool: Yes
  16. def _(val):

    52@format_value.register(list)53def _(val[1, 2, 3, 4, 5]):54    return f"[{len(val[1, 2, 3, 4, 5])} items]"
  17. data ← {'name': 'Alice', 'age': 30, 'scores': [85, 92, 78], 'active': True}

    58print(f"Bool: {format_value(True)}")59print(f"List: {format_value([1, 2, 3, 4, 5])}")6061# Serialize function62print("\nSerialize function:")6364@singledispatch65def serialize(obj):66    """Default serialization."""67    raise NotImplementedError(f"Cannot serialize {type(obj)}")6869@serialize.register(int)70@serialize.register(float)71@serialize.register(str)72def _(obj):73    return obj7475@serialize.register(list)76def _(obj):77    return [serialize(item) for item in obj]7879@serialize.register(dict)80def _(obj):81    return {key: serialize(value) for key, value in obj.items()}8283data→ {'name': 'Alice', 'age': 30, 'scores': [85, 92, 78], 'active': True} = {84    'name': 'Alice',85    'age': 30,86    'scores': [85, 92, 78],87    'active': True88}
    outputList: [5 items]
    
    Serialize function:
  18. try:

    90try:91    result = serialize(data{'name': 'Alice', 'age': 30, 'scores': [85, 92, 78], 'active': True})92    print(f"Serialized: {result}")
  19. def _(obj):

    79@serialize.register(dict)80def _(obj{'name': 'Alice', 'age': 30, 'scores': [85, 92, 78], 'active': True}):81    return {key(empty): serialize(value(empty)) for key, value in obj{'name': 'Alice', 'age': 30, 'scores': [85, 92, 78], 'active': True}.items()}
  20. def _(obj):

    pass 1 of 6
    71@serialize.register(str)72def _(objAlice):73    return objAlice
    All 6 passes — pass 1 is the card above
    passobj
    1Alice
    230
    385
    492
    578
    6True
  21. def _(obj):

    75@serialize.register(list)76def _(obj[85, 92, 78]):77    return [serialize(item) for item in obj[85, 92, 78]]
  22. result ← {'name': 'Alice', 'age': 30, 'scores': [85, 92, 78], 'active': True}

    90try:91    result→ {'name': 'Alice', 'age': 30, 'scores': [85, 92, 78], 'active': True} = serialize(data{'name': 'Alice', 'age': 30, 'scores': [85, 92, 78], 'active': True})92    print(f"Serialized: {result{'name': 'Alice', 'age': 30, 'scores': [85, 92, 78], 'active': True}}")93except NotImplementedError as e:
    outputSerialized: {'name': 'Alice', 'age': 30, 'scores': [85, 92, 78], 'active': True}
  23. print(" Area calculation:")

    96# Area calculation97print("\nArea calculation:")9899@singledispatch100def area(shape):101    """Calculate area of shape."""102    raise NotImplementedError(f"Unknown shape: {type(shape)}")103104class Circle:105    def __init__(self, radius):106        self.radius = radius107108class Rectangle:109    def __init__(self, width, height):110        self.width = width111        self.height = height112113class Triangle:114    def __init__(self, base, height):115        self.base = base116        self.height = height117118@area.register(Circle)119def _(shape):120    import math121    return math.pi * shape.radius ** 2122123@area.register(Rectangle)124def _(shape):125    return shape.width * shape.height126127@area.register(Triangle)128def _(shape):129    return 0.5 * shape.base * shape.height130131# Calculate areas132circle = Circle(5)133rectangle = Rectangle(4, 6)
    output
    Area calculation:
  24. self.radius ← 5

    104class Circle:105    def __init__(self⟨Circle A⟩, radius5):106        self.radius→ 5 = radius5
  25. circle ← ⟨Circle A⟩

    131# Calculate areas132circle→ ⟨Circle A⟩ = Circle(5)133rectangle = Rectangle(4, 6)134triangle = Triangle(3, 8)
  26. self.width ← 4, self.height ← 6

    108class Rectangle:109    def __init__(self⟨Rectangle B⟩, width4, height6):110        self.width→ 4 = width4111        self.height→ 6 = height6
  27. rectangle ← ⟨Rectangle B⟩

    132circle = Circle(5)133rectangle→ ⟨Rectangle B⟩ = Rectangle(4, 6)134triangle = Triangle(3, 8)
  28. self.base ← 3, self.height ← 8

    113class Triangle:114    def __init__(self⟨Triangle C⟩, base3, height8):115        self.base→ 3 = base3116        self.height→ 8 = height8
  29. triangle ← ⟨Triangle C⟩

    133rectangle = Rectangle(4, 6)134triangle→ ⟨Triangle C⟩ = Triangle(3, 8)135136print(f"Circle area: {area(circle⟨Circle A⟩):.2f}")137print(f"Rectangle area: {area(rectangle):.2f}")
  30. def _(shape):

    118@area.register(Circle)119def _(shape⟨Circle A⟩):120    import math121    return math.pi3.141592653589793 * shape.radius5 ** 2
  31. print(f"Circle area: {area(circle):.2f}")

    136print(f"Circle area: {area(circle⟨Circle A⟩):.2f}")137print(f"Rectangle area: {area(rectangle⟨Rectangle B⟩):.2f}")138print(f"Triangle area: {area(triangle):.2f}")
    outputCircle area: 78.54
  32. def _(shape):

    123@area.register(Rectangle)124def _(shape⟨Rectangle B⟩):125    return shape.width4 * shape.height6
  33. print(f"Rectangle area: {area(rectangle):.2f}")

    136print(f"Circle area: {area(circle):.2f}")137print(f"Rectangle area: {area(rectangle⟨Rectangle B⟩):.2f}")138print(f"Triangle area: {area(triangle⟨Triangle C⟩):.2f}")
    outputRectangle area: 24.00
  34. def _(shape):

    127@area.register(Triangle)128def _(shape⟨Triangle C⟩):129    return 0.5 * shape.base3 * shape.height8
  35. print(f"Triangle area: {area(triangle):.2f}")

    137print(f"Rectangle area: {area(rectangle):.2f}")138print(f"Triangle area: {area(triangle⟨Triangle C⟩):.2f}")139140# Type conversion141print("\nType conversion:")142143@singledispatch144def to_string(val):145    """Convert to string."""146    return str(val)147148@to_string.register(int)149def _(val):150    return f"Integer: {val}"151152@to_string.register(float)153def _(val):154    return f"Float: {val:.4f}"155156@to_string.register(Decimal)157def _(val):158    return f"Decimal: {val}"159160@to_string.register(list)161def _(val):162    return f"List with {len(val)} elements"163164print(to_string(42))165print(to_string(3.14159))
    outputTriangle area: 12.00
    
    Type conversion:
  36. def _(val):

    148@to_string.register(int)149def _(val42):150    return f"Integer: {val42}"
  37. print(to_string(42))

    164print(to_string(42))165print(to_string(3.14159))166print(to_string(Decimal('10.50')))
    outputInteger: 42
  38. def _(val):

    152@to_string.register(float)153def _(val3.14159):154    return f"Float: {val3.14159:.4f}"
  39. print(to_string(3.14159))

    164print(to_string(42))165print(to_string(3.14159))166print(to_string(Decimal('10.50')))167print(to_string([1, 2, 3]))
    outputFloat: 3.1416
  40. def _(val):

    156@to_string.register(Decimal)157def _(val10.50):158    return f"Decimal: {val10.50}"
  41. print(to_string(Decimal('10.50')))

    165print(to_string(3.14159))166print(to_string(Decimal('10.50')))167print(to_string([1, 2, 3]))
    outputDecimal: 10.50
  42. def _(val):

    160@to_string.register(list)161def _(val[1, 2, 3]):162    return f"List with {len(val[1, 2, 3])} elements"
  43. print(to_string([1, 2, 3]))

    166print(to_string(Decimal('10.50')))167print(to_string([1, 2, 3]))168169# HTML rendering170print("\nHTML rendering:")171172@singledispatch173def to_html(data):174    """Render data as HTML."""175    return f"<span>{data}</span>"176177@to_html.register(int)178@to_html.register(float)179def _(data):180    return f"<strong>{data}</strong>"181182@to_html.register(str)183def _(data):184    return f"<p>{data}</p>"185186@to_html.register(list)187def _(data):188    items = ''.join(f"<li>{to_html(item)}</li>" for item in data)189    return f"<ul>{items}</ul>"190191@to_html.register(dict)192def _(data):193    rows = ''.join(194        f"<tr><td>{key}</td><td>{to_html(value)}</td></tr>"195        for key, value in data.items()196    )197    return f"<table>{rows}</table>"198199print(to_html("Hello"))200print(to_html(42))
    outputList with 3 elements
    
    HTML rendering:
  44. def _(data):

    182@to_html.register(str)183def _(dataHello):184    return f"<p>{dataHello}</p>"
  45. print(to_html("Hello"))

    199print(to_html("Hello"))200print(to_html(42))201print(to_html([1, 2, 3]))
    output<p>Hello</p>
  46. def _(data):

    pass 1 of 4
    178@to_html.register(float)179def _(data42):180    return f"<strong>{data42}</strong>"
    All 4 passes — pass 1 is the card above
    passdata
    142
    21
    32
    43
  47. print(to_html(42))

    199print(to_html("Hello"))200print(to_html(42))201print(to_html([1, 2, 3]))
    output<strong>42</strong>
  48. def _(data):

    186@to_html.register(list)187def _(data[1, 2, 3]):188    items = ''.join(f"<li>{to_html(item)}</li>" for item in data[1, 2, 3])189    return f"<ul>{items}</ul>"
  49. items ← <li><strong>1</strong></li><li><strong>2</strong></li><li><strong>3</strong></li>

    187def _(data):188    items→ <li><strong>1</strong></li><li><strong>2</strong></li><li><strong>3</strong></li> = ''.join(f"<li>{to_html(item)}</li>" for item in data[1, 2, 3])189    return f"<ul>{items<li><strong>1</strong></li><li><strong>2</strong></li><li><strong>3</strong></li>}</ul>"
  50. print(to_html([1, 2, 3]))

    200print(to_html(42))201print(to_html([1, 2, 3]))202203# Validator204print("\nValidator:")205206@singledispatch207def validate(val):208    """Validate value."""209    return True210211@validate.register(str)212def _(val):213    return len(val) > 0214215@validate.register(int)216def _(val):217    return val >= 0218219@validate.register(list)220def _(val):221    return len(val) > 0 and all(validate(item) for item in val)222223print(f"Validate 'hello': {validate('hello')}")224print(f"Validate '': {validate('')}")
    output<ul><li><strong>1</strong></li><li><strong>2</strong></li><li><strong>3</strong></li></ul>
    
    Validator:
  51. def _(val):

    pass 1 of 2
    211@validate.register(str)212def _(valhello):213    return len(valhello) > 0
  52. print(f"Validate 'hello': {validate('hello')}")

    223print(f"Validate 'hello': {validate('hello')}")224print(f"Validate '': {validate('')}")225print(f"Validate 10: {validate(10)}")
    outputValidate 'hello': True
  53. def _(val):

    pass 2 of 2
    211@validate.register(str)212def _(val(empty)):213    return len(val(empty)) > 0
  54. print(f"Validate '': {validate('')}")

    223print(f"Validate 'hello': {validate('hello')}")224print(f"Validate '': {validate('')}")225print(f"Validate 10: {validate(10)}")226print(f"Validate -5: {validate(-5)}")
    outputValidate '': False
  55. def _(val):

    pass 1 of 4
    215@validate.register(int)216def _(val10):217    return val10 >= 0
    All 4 passes — pass 1 is the card above
    passval
    110
    2-5
    31
    42
  56. print(f"Validate 10: {validate(10)}")

    224print(f"Validate '': {validate('')}")225print(f"Validate 10: {validate(10)}")226print(f"Validate -5: {validate(-5)}")227print(f"Validate [1, 2]: {validate([1, 2])}")
    outputValidate 10: True
  57. print(f"Validate -5: {validate(-5)}")

    225print(f"Validate 10: {validate(10)}")226print(f"Validate -5: {validate(-5)}")227print(f"Validate [1, 2]: {validate([1, 2])}")
    outputValidate -5: False
  58. def _(val):

    219@validate.register(list)220def _(val[1, 2]):221    return len(val[1, 2]) > 0 and all(validate(item) for item in val)
  59. print(f"Registry: {demo.registry.keys()}")

    226print(f"Validate -5: {validate(-5)}")227print(f"Validate [1, 2]: {validate([1, 2])}")228229# Type dispatch info230print("\nType dispatch info:")231232@singledispatch233def demo(arg):234    return "default"235236@demo.register(int)237def _(arg):238    return "int"239240@demo.register(str)241def _(arg):242    return "str"243244# Check registered types245print(f"Registry: {demo.registry{<class 'object'>: ⟨function demo D⟩, <class 'int'>: ⟨function _ E⟩, <class 'str'>: ⟨function _ F⟩}.keys()}")246print(f"Dispatch(int): {demo⟨function demo G⟩.dispatch(int)}")247print(f"Dispatch(str): {demo⟨function demo G⟩.dispatch(str)}")
    outputValidate [1, 2]: True
    
    Type dispatch info:
    Registry: dict_keys([<class 'object'>, <class 'int'>, <class 'str'>])
    Dispatch(int): ⟨function _ E⟩
    Dispatch(str): ⟨function _ F⟩

Total Ordering

Generate comparison methods automatically:

ordering.py
Replay: real traced execution (multi-file project)
# functools.total_ordering examples

from functools import total_ordering

# Basic total_ordering
print("Basic total_ordering:")

@total_ordering
class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    def __eq__(self, other):
        return self.grade == other.grade

    def __lt__(self, other):
        return self.grade < other.grade

    def __repr__(self):
        return f"Student('{self.name}', {self.grade})"

# Create students
alice = Student("Alice", 85)
bob = Student("Bob", 92)
charlie = Student("Charlie", 85)

# All comparison operations work
print(f"alice < bob: {alice < bob}")
print(f"alice <= bob: {alice <= bob}")
print(f"alice > bob: {alice > bob}")
print(f"alice >= bob: {alice >= bob}")
print(f"alice == charlie: {alice == charlie}")
print(f"alice != bob: {alice != bob}")

# Sorting
print("\nSorting:")

students = [
    Student("David", 78),
    Student("Eve", 95),
    Student("Frank", 82),
    Student("Grace", 88)
]

print("Unsorted:")
for s in students:
    print(f"  {s}")

sorted_students = sorted(students)

print("Sorted by grade:")
for s in sorted_students:
    print(f"  {s}")

# Temperature class
print("\nTemperature class:")

@total_ordering
class Temperature:
    def __init__(self, celsius):
        self.celsius = celsius

    def __eq__(self, other):
        return self.celsius == other.celsius

    def __lt__(self, other):
        return self.celsius < other.celsius

    def __repr__(self):
        return f"{self.celsius}°C"

temps = [
    Temperature(20),
    Temperature(5),
    Temperature(30),
    Temperature(15)
]

print(f"Temperatures: {temps}")
print(f"Sorted: {sorted(temps)}")
print(f"Min: {min(temps)}")
print(f"Max: {max(temps)}")

# Version class
print("\nVersion class:")

@total_ordering
class Version:
    def __init__(self, version_string):
        self.parts = tuple(map(int, version_string.split('.')))

    def __eq__(self, other):
        return self.parts == other.parts

    def __lt__(self, other):
        return self.parts < other.parts

    def __repr__(self):
        return '.'.join(map(str, self.parts))

versions = [
    Version("1.2.3"),
    Version("1.10.0"),
    Version("1.2.10"),
    Version("2.0.0")
]

print(f"Versions: {versions}")
print(f"Sorted: {sorted(versions)}")

# Money class
print("\nMoney class:")

@total_ordering
class Money:
    def __init__(self, amount, currency='USD'):
        self.amount = amount
        self.currency = currency

    def __eq__(self, other):
        if self.currency != other.currency:
            raise ValueError("Cannot compare different currencies")
        return self.amount == other.amount

    def __lt__(self, other):
        if self.currency != other.currency:
            raise ValueError("Cannot compare different currencies")
        return self.amount < other.amount

    def __repr__(self):
        return f"${self.amount:.2f} {self.currency}"

prices = [
    Money(19.99),
    Money(5.99),
    Money(12.50),
    Money(25.00)
]

print(f"Prices: {prices}")
print(f"Cheapest: {min(prices)}")
print(f"Most expensive: {max(prices)}")

# Date class
print("\nDate class:")

@total_ordering
class SimpleDate:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    def __eq__(self, other):
        return (self.year, self.month, self.day) == \
               (other.year, other.month, other.day)

    def __lt__(self, other):
        return (self.year, self.month, self.day) < \
               (other.year, other.month, other.day)

    def __repr__(self):
        return f"{self.year}-{self.month:02d}-{self.day:02d}"

dates = [
    SimpleDate(2024, 5, 15),
    SimpleDate(2023, 12, 1),
    SimpleDate(2024, 1, 10),
    SimpleDate(2024, 5, 1)
]

print(f"Dates: {dates}")
print(f"Sorted: {sorted(dates)}")

# Range checking
print("\nRange checking:")

@total_ordering
class Score:
    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        return self.value == other.value

    def __lt__(self, other):
        return self.value < other.value

    def __repr__(self):
        return f"Score({self.value})"

score = Score(75)
min_score = Score(60)
max_score = Score(100)

print(f"Score: {score}")
print(f"In range [60, 100]? {min_score <= score <= max_score}")

# Priority
print("\nPriority:")

@total_ordering
class Task:
    PRIORITIES = {'low': 3, 'medium': 2, 'high': 1}

    def __init__(self, name, priority):
        self.name = name
        self.priority = priority

    def __eq__(self, other):
        return self.PRIORITIES[self.priority] == \
               self.PRIORITIES[other.priority]

    def __lt__(self, other):
        return self.PRIORITIES[self.priority] < \
               self.PRIORITIES[other.priority]

    def __repr__(self):
        return f"Task('{self.name}', '{self.priority}')"

tasks = [
    Task("Write docs", "low"),
    Task("Fix bug", "high"),
    Task("Review code", "medium"),
    Task("Deploy", "high")
]

print("Tasks:")
for t in tasks:
    print(f"  {t}")

print("\nSorted by priority:")
for t in sorted(tasks):
    print(f"  {t}")

  1. print("Basic total_ordering:")

    5# Basic total_ordering6print("Basic total_ordering:")78@total_ordering9class Student:10    def __init__(self, name, grade):11        self.name = name12        self.grade = grade13    14    def __eq__(self, other):15        return self.grade == other.grade16    17    def __lt__(self, other):18        return self.grade < other.grade19    20    def __repr__(self):21        return f"Student('{self.name}', {self.grade})"2223# Create students24alice = Student("Alice", 85)25bob = Student("Bob", 92)
    outputBasic total_ordering:
  2. self.name ← Alice, self.grade ← 85

    pass 1 of 7
    9class Student:10    def __init__(self(empty), nameAlice, grade85):11        self.name→ Alice = nameAlice12        self.grade→ 85 = grade85
    All 7 passes — pass 1 is the card above
    passnamegradeself.nameself.grade
    1Alice85Alice85
    2Bob92Bob92
    3Charlie85Charlie85
    4David78David78
    5Eve95Eve95
    6Frank82Frank82
    7Grace88Grace88
  3. alice ← Student('Alice', 85)

    23# Create students24alice→ Student('Alice', 85) = Student("Alice", 85)25bob = Student("Bob", 92)26charlie = Student("Charlie", 85)
  4. bob ← Student('Bob', 92)

    24alice = Student("Alice", 85)25bob→ Student('Bob', 92) = Student("Bob", 92)26charlie = Student("Charlie", 85)
  5. charlie ← Student('Charlie', 85)

    25bob = Student("Bob", 92)26charlie→ Student('Charlie', 85) = Student("Charlie", 85)2728# All comparison operations work29print(f"alice < bob: {aliceStudent('Alice', 85) < bobStudent('Bob', 92)}")30print(f"alice <= bob: {alice <= bob}")
  6. def __lt__(self, other):

    pass 1 of 10
    17def __lt__(selfStudent('Alice', 85), otherStudent('Bob', 92)):18    return self.grade85 < other.grade92
    All 10 passes — pass 1 is the card above
    passselfotherself.gradeother.grade
    1Student('Alice', 85)Student('Bob', 92)8592
    2Student('Alice', 85)Student('Bob', 92)8592
    3Student('Alice', 85)Student('Bob', 92)8592
    4Student('Alice', 85)Student('Bob', 92)8592
    5Student('Eve', 95)Student('David', 78)9578
    6Student('Frank', 82)Student('Eve', 95)8295
    7Student('Frank', 82)Student('Eve', 95)8295
    8Student('Frank', 82)Student('David', 78)8278
    9Student('Grace', 88)Student('Frank', 82)8882
    10Student('Grace', 88)Student('Eve', 95)8895
  7. print(f"alice < bob: {alice < bob}")

    28# All comparison operations work29print(f"alice < bob: {aliceStudent('Alice', 85) < bobStudent('Bob', 92)}")30print(f"alice <= bob: {aliceStudent('Alice', 85) <= bobStudent('Bob', 92)}")31print(f"alice > bob: {alice > bob}")
    outputalice < bob: True
  8. print(f"alice <= bob: {alice <= bob}")

    29print(f"alice < bob: {alice < bob}")30print(f"alice <= bob: {aliceStudent('Alice', 85) <= bobStudent('Bob', 92)}")31print(f"alice > bob: {aliceStudent('Alice', 85) > bobStudent('Bob', 92)}")32print(f"alice >= bob: {alice >= bob}")
    outputalice <= bob: True
  9. print(f"alice > bob: {alice > bob}")

    30print(f"alice <= bob: {alice <= bob}")31print(f"alice > bob: {aliceStudent('Alice', 85) > bobStudent('Bob', 92)}")32print(f"alice >= bob: {aliceStudent('Alice', 85) >= bobStudent('Bob', 92)}")33print(f"alice == charlie: {alice == charlie}")
    outputalice > bob: False
  10. print(f"alice >= bob: {alice >= bob}")

    31print(f"alice > bob: {alice > bob}")32print(f"alice >= bob: {aliceStudent('Alice', 85) >= bobStudent('Bob', 92)}")33print(f"alice == charlie: {aliceStudent('Alice', 85) == charlieStudent('Charlie', 85)}")34print(f"alice != bob: {alice != bob}")
    outputalice >= bob: False
  11. def __eq__(self, other):

    pass 1 of 2
    14def __eq__(selfStudent('Alice', 85), otherStudent('Charlie', 85)):15    return self.grade85 == other.grade85
  12. print(f"alice == charlie: {alice == charlie}")

    32print(f"alice >= bob: {alice >= bob}")33print(f"alice == charlie: {aliceStudent('Alice', 85) == charlieStudent('Charlie', 85)}")34print(f"alice != bob: {aliceStudent('Alice', 85) != bobStudent('Bob', 92)}")
    outputalice == charlie: True
  13. def __eq__(self, other):

    pass 2 of 2
    14def __eq__(selfStudent('Alice', 85), otherStudent('Bob', 92)):15    return self.grade85 == other.grade92
  14. print(f"alice != bob: {alice != bob}")

    33print(f"alice == charlie: {alice == charlie}")34print(f"alice != bob: {aliceStudent('Alice', 85) != bobStudent('Bob', 92)}")3536# Sorting37print("\nSorting:")3839students = [40    Student("David", 78),41    Student("Eve", 95),42    Student("Frank", 82),43    Student("Grace", 88)44]
    outputalice != bob: True
    
    Sorting:
  15. students ← [Student('David', 78), Student('Eve', 95), Student('Frank', 82), Student('Grace', 88)]

    39students→ [Student('David', 78), Student('Eve', 95), Student('Frank', 82), Student('Grace', 88)] = [40    Student("David", 78),41    Student("Eve", 95),42    Student("Frank", 82),43    Student("Grace", 88)44]4546print("Unsorted:")47for s in students:
    outputUnsorted:
  16. for s in students:

    pass 1 of 4
    46print("Unsorted:")47for sStudent('David', 78) in students[Student('David', 78), Student('Eve', 95), Student('Frank', 82), Student('Grace', 88)]:48    print(f"  {sStudent('David', 78)}")
    output  Student('David', 78)
    All 4 passes — pass 1 is the card above
    passs
    1Student('David', 78)
    2Student('Eve', 95)
    3Student('Frank', 82)
    4Student('Grace', 88)
  17. sorted_students = sorted(students)

    50sorted_students = sorted(students[Student('David', 78), Student('Eve', 95), Student('Frank', 82), Student('Grace', 88)])
  18. sorted_students ← [Student('David', 78), Student('Frank', 82), Student('Grace', 88), Student('Eve', 95)]

    50sorted_students→ [Student('David', 78), Student('Frank', 82), Student('Grace', 88), Student('Eve', 95)] = sorted(students[Student('David', 78), Student('Eve', 95), Student('Frank', 82), Student('Grace', 88)])5152print("Sorted by grade:")53for s in sorted_students:
    outputSorted by grade:
  19. for s in sorted_students:

    pass 1 of 4
    52print("Sorted by grade:")53for sStudent('David', 78) in sorted_students[Student('David', 78), Student('Frank', 82), Student('Grace', 88), Student('Eve', 95)]:54    print(f"  {sStudent('David', 78)}")
    output  Student('David', 78)
    All 4 passes — pass 1 is the card above
    passs
    1Student('David', 78)
    2Student('Frank', 82)
    3Student('Grace', 88)
    4Student('Eve', 95)
  20. print(" Temperature class:")

    56# Temperature class57print("\nTemperature class:")5859@total_ordering60class Temperature:61    def __init__(self, celsius):62        self.celsius = celsius63    64    def __eq__(self, other):65        return self.celsius == other.celsius66    67    def __lt__(self, other):68        return self.celsius < other.celsius69    70    def __repr__(self):71        return f"{self.celsius}°C"7273temps = [74    Temperature(20),75    Temperature(5),76    Temperature(30),77    Temperature(15)78]
    output
    Temperature class:
  21. self.celsius ← 20

    pass 1 of 4
    60class Temperature:61    def __init__(self(empty), celsius20):62        self.celsius→ 20 = celsius20
    All 4 passes — pass 1 is the card above
    passcelsiusself.celsius
    12020
    255
    33030
    41515
  22. temps ← [20°C, 5°C, 30°C, 15°C]

    73temps→ [20°C, 5°C, 30°C, 15°C] = [74    Temperature(20),75    Temperature(5),76    Temperature(30),77    Temperature(15)78]7980print(f"Temperatures: {temps[20°C, 5°C, 30°C, 15°C]}")81print(f"Sorted: {sorted(temps[20°C, 5°C, 30°C, 15°C])}")82print(f"Min: {min(temps)}")
    outputTemperatures: [20°C, 5°C, 30°C, 15°C]
  23. def __lt__(self, other):

    pass 1 of 11
    67def __lt__(self5°C, other20°C):68    return self.celsius5 < other.celsius20
    All 11 passes — pass 1 is the card above
    passselfotherself.celsiusother.celsius
    15°C20°C520
    230°C5°C305
    330°C20°C3020
    415°C20°C1520
    515°C5°C155
    65°C20°C520
    730°C5°C305
    815°C5°C155
    95°C20°C520
    1030°C20°C3020
    1115°C30°C1530
  24. print(f"Sorted: {sorted(temps)}")

    80print(f"Temperatures: {temps}")81print(f"Sorted: {sorted(temps[20°C, 5°C, 30°C, 15°C])}")82print(f"Min: {min(temps[20°C, 5°C, 30°C, 15°C])}")83print(f"Max: {max(temps)}")
    outputSorted: [5°C, 15°C, 20°C, 30°C]
  25. print(f"Min: {min(temps)}")

    81print(f"Sorted: {sorted(temps)}")82print(f"Min: {min(temps[20°C, 5°C, 30°C, 15°C])}")83print(f"Max: {max(temps[20°C, 5°C, 30°C, 15°C])}")
    outputMin: 5°C
  26. def __eq__(self, other):

    64def __eq__(self30°C, other20°C):65    return self.celsius30 == other.celsius20
  27. print(f"Max: {max(temps)}")

    82print(f"Min: {min(temps)}")83print(f"Max: {max(temps[20°C, 5°C, 30°C, 15°C])}")8485# Version class86print("\nVersion class:")8788@total_ordering89class Version:90    def __init__(self, version_string):91        self.parts = tuple(map(int, version_string.split('.')))92    93    def __eq__(self, other):94        return self.parts == other.parts95    96    def __lt__(self, other):97        return self.parts < other.parts98    99    def __repr__(self):100        return '.'.join(map(str, self.parts))101102versions = [103    Version("1.2.3"),104    Version("1.10.0"),105    Version("1.2.10"),106    Version("2.0.0")107]
    outputMax: 30°C
    
    Version class:
  28. self.parts ← (1, 2, 3)

    pass 1 of 4
    89class Version:90    def __init__(self(empty), version_string1.2.3):91        self.parts→ (1, 2, 3) = tuple(map(int, version_string1.2.3.split('.')))
    All 4 passes — pass 1 is the card above
    passversion_stringself.parts
    11.2.3(1, 2, 3)
    21.10.0(1, 10, 0)
    31.2.10(1, 2, 10)
    42.0.0(2, 0, 0)
  29. versions ← [1.2.3, 1.10.0, 1.2.10, 2.0.0]

    102versions→ [1.2.3, 1.10.0, 1.2.10, 2.0.0] = [103    Version("1.2.3"),104    Version("1.10.0"),105    Version("1.2.10"),106    Version("2.0.0")107]108109print(f"Versions: {versions[1.2.3, 1.10.0, 1.2.10, 2.0.0]}")110print(f"Sorted: {sorted(versions[1.2.3, 1.10.0, 1.2.10, 2.0.0])}")
    outputVersions: [1.2.3, 1.10.0, 1.2.10, 2.0.0]
  30. def __lt__(self, other):

    pass 1 of 6
    96def __lt__(self1.10.0, other1.2.3):97    return self.parts(1, 10, 0) < other.parts(1, 2, 3)
    All 6 passes — pass 1 is the card above
    passselfotherself.partsother.parts
    11.10.01.2.3(1, 10, 0)(1, 2, 3)
    21.2.101.10.0(1, 2, 10)(1, 10, 0)
    31.2.101.10.0(1, 2, 10)(1, 10, 0)
    41.2.101.2.3(1, 2, 10)(1, 2, 3)
    52.0.01.2.10(2, 0, 0)(1, 2, 10)
    62.0.01.10.0(2, 0, 0)(1, 10, 0)
  31. print(f"Sorted: {sorted(versions)}")

    109print(f"Versions: {versions}")110print(f"Sorted: {sorted(versions[1.2.3, 1.10.0, 1.2.10, 2.0.0])}")111112# Money class113print("\nMoney class:")114115@total_ordering116class Money:117    def __init__(self, amount, currency='USD'):118        self.amount = amount119        self.currency = currency120    121    def __eq__(self, other):122        if self.currency != other.currency:123            raise ValueError("Cannot compare different currencies")124        return self.amount == other.amount125    126    def __lt__(self, other):127        if self.currency != other.currency:128            raise ValueError("Cannot compare different currencies")129        return self.amount < other.amount130    131    def __repr__(self):132        return f"${self.amount:.2f} {self.currency}"133134prices = [135    Money(19.99),136    Money(5.99),137    Money(12.50),138    Money(25.00)139]
    outputSorted: [1.2.3, 1.2.10, 1.10.0, 2.0.0]
    
    Money class:
  32. self.amount ← 19.99, self.currency ← USD

    pass 1 of 4
    116class Money:117    def __init__(self(empty), amount19.99, currencyUSD='USD'):118        self.amount→ 19.99 = amount19.99119        self.currency→ USD = currencyUSD
    All 4 passes — pass 1 is the card above
    passamountself.amountself.currency
    119.9919.99USD
    25.995.99USD
    312.512.5USD
    425.025.0USD
  33. prices ← [$19.99 USD, $5.99 USD, $12.50 USD, $25.00 USD]

    134prices→ [$19.99 USD, $5.99 USD, $12.50 USD, $25.00 USD] = [135    Money(19.99),136    Money(5.99),137    Money(12.50),138    Money(25.00)139]140141print(f"Prices: {prices[$19.99 USD, $5.99 USD, $12.50 USD, $25.00 USD]}")142print(f"Cheapest: {min(prices[$19.99 USD, $5.99 USD, $12.50 USD, $25.00 USD])}")143print(f"Most expensive: {max(prices)}")
    outputPrices: [$19.99 USD, $5.99 USD, $12.50 USD, $25.00 USD]
  34. def __lt__(self, other):

    pass 1 of 6
    126def __lt__(self$5.99 USD, other$19.99 USD):127    if self.currency != other.currency:128        raise ValueError("Cannot compare different currencies")129    return self.amount5.99 < other.amount19.99
    All 6 passes — pass 1 is the card above
    passselfotherself.amountother.amount
    1$5.99 USD$19.99 USD5.9919.99
    2$12.50 USD$5.99 USD12.55.99
    3$25.00 USD$5.99 USD25.05.99
    4$5.99 USD$19.99 USD5.9919.99
    5$12.50 USD$19.99 USD12.519.99
    6$25.00 USD$19.99 USD25.019.99
  35. print(f"Cheapest: {min(prices)}")

    141print(f"Prices: {prices}")142print(f"Cheapest: {min(prices[$19.99 USD, $5.99 USD, $12.50 USD, $25.00 USD])}")143print(f"Most expensive: {max(prices[$19.99 USD, $5.99 USD, $12.50 USD, $25.00 USD])}")
    outputCheapest: $5.99 USD
  36. def __eq__(self, other):

    121def __eq__(self$25.00 USD, other$19.99 USD):122    if self.currency != other.currency:123        raise ValueError("Cannot compare different currencies")124    return self.amount25.0 == other.amount19.99
  37. print(f"Most expensive: {max(prices)}")

    142print(f"Cheapest: {min(prices)}")143print(f"Most expensive: {max(prices[$19.99 USD, $5.99 USD, $12.50 USD, $25.00 USD])}")144145# Date class146print("\nDate class:")147148@total_ordering149class SimpleDate:150    def __init__(self, year, month, day):151        self.year = year152        self.month = month153        self.day = day154    155    def __eq__(self, other):156        return (self.year, self.month, self.day) == \157               (other.year, other.month, other.day)158    159    def __lt__(self, other):160        return (self.year, self.month, self.day) < \161               (other.year, other.month, other.day)162    163    def __repr__(self):164        return f"{self.year}-{self.month:02d}-{self.day:02d}"165166dates = [167    SimpleDate(2024, 5, 15),168    SimpleDate(2023, 12, 1),169    SimpleDate(2024, 1, 10),170    SimpleDate(2024, 5, 1)171]
    outputMost expensive: $25.00 USD
    
    Date class:
  38. self.year ← 2024, self.month ← 5, self.day ← 15

    pass 1 of 4
    149class SimpleDate:150    def __init__(self(empty), year2024, month5, day15):151        self.year→ 2024 = year2024152        self.month→ 5 = month5153        self.day→ 15 = day15
    All 4 passes — pass 1 is the card above
    passyearmonthdayself.yearself.monthself.day
    120245152024515
    220231212023121
    320241102024110
    4202451202451
  39. dates ← [2024-05-15, 2023-12-01, 2024-01-10, 2024-05-01]

    166dates→ [2024-05-15, 2023-12-01, 2024-01-10, 2024-05-01] = [167    SimpleDate(2024, 5, 15),168    SimpleDate(2023, 12, 1),169    SimpleDate(2024, 1, 10),170    SimpleDate(2024, 5, 1)171]172173print(f"Dates: {dates[2024-05-15, 2023-12-01, 2024-01-10, 2024-05-01]}")174print(f"Sorted: {sorted(dates[2024-05-15, 2023-12-01, 2024-01-10, 2024-05-01])}")
    outputDates: [2024-05-15, 2023-12-01, 2024-01-10, 2024-05-01]
  40. def __lt__(self, other):

    pass 1 of 6
    159def __lt__(self2023-12-01, other2024-05-15):160    return (self.year2023, self.month12, self.day1) < \161           (other.year2024, other.month5, other.day15)
    All 6 passes — pass 1 is the card above
    passselfotherself.yearself.monthself.dayother.yearother.monthother.day
    12023-12-012024-05-1520231212024515
    22024-01-102023-12-0120241102023121
    32024-01-102024-05-1520241102024515
    42024-01-102023-12-0120241102023121
    52024-05-012024-01-102024512024110
    62024-05-012024-05-152024512024515
  41. print(f"Sorted: {sorted(dates)}")

    173print(f"Dates: {dates}")174print(f"Sorted: {sorted(dates[2024-05-15, 2023-12-01, 2024-01-10, 2024-05-01])}")175176# Range checking177print("\nRange checking:")178179@total_ordering180class Score:181    def __init__(self, value):182        self.value = value183    184    def __eq__(self, other):185        return self.value == other.value186    187    def __lt__(self, other):188        return self.value < other.value189    190    def __repr__(self):191        return f"Score({self.value})"192193score = Score(75)194min_score = Score(60)
    outputSorted: [2023-12-01, 2024-01-10, 2024-05-01, 2024-05-15]
    
    Range checking:
  42. self.value ← 75

    pass 1 of 3
    180class Score:181    def __init__(self(empty), value75):182        self.value→ 75 = value75
    All 3 passes — pass 1 is the card above
    passvalueself.value
    17575
    26060
    3100100
  43. score ← Score(75)

    193score→ Score(75) = Score(75)194min_score = Score(60)195max_score = Score(100)
  44. min_score ← Score(60)

    193score = Score(75)194min_score→ Score(60) = Score(60)195max_score = Score(100)
  45. max_score ← Score(100)

    194min_score = Score(60)195max_score→ Score(100) = Score(100)196197print(f"Score: {scoreScore(75)}")198print(f"In range [60, 100]? {min_scoreScore(60) <= scoreScore(75) <= max_scoreScore(100)}")
    outputScore: Score(75)
  46. def __lt__(self, other):

    pass 1 of 2
    187def __lt__(selfScore(60), otherScore(75)):188    return self.value60 < other.value75
  47. def __lt__(self, other):

    pass 2 of 2
    187def __lt__(selfScore(75), otherScore(100)):188    return self.value75 < other.value100
  48. PRIORITIES ← (empty)

    197print(f"Score: {score}")198print(f"In range [60, 100]? {min_scoreScore(60) <= scoreScore(75) <= max_scoreScore(100)}")199200# Priority201print("\nPriority:")202203@total_ordering204class Task:205    PRIORITIES→ (empty) = {'low': 3, 'medium': 2, 'high': 1}206    207    def __init__(self, name, priority):208        self.name = name209        self.priority = priority210    211    def __eq__(self, other):212        return self.PRIORITIES[self.priority] == \213               self.PRIORITIES[other.priority]214    215    def __lt__(self, other):216        return self.PRIORITIES[self.priority] < \217               self.PRIORITIES[other.priority]218    219    def __repr__(self):220        return f"Task('{self.name}', '{self.priority}')"221222tasks = [223    Task("Write docs", "low"),224    Task("Fix bug", "high"),225    Task("Review code", "medium"),226    Task("Deploy", "high")227]
    outputIn range [60, 100]? True
    
    Priority:
  49. self.name ← Write docs, self.priority ← low

    pass 1 of 4
    207def __init__(self(empty), nameWrite docs, prioritylow):208    self.name→ Write docs = nameWrite docs209    self.priority→ low = prioritylow
    All 4 passes — pass 1 is the card above
    passnamepriorityself.nameself.priority
    1Write docslowWrite docslow
    2Fix bughighFix bughigh
    3Review codemediumReview codemedium
    4DeployhighDeployhigh
  50. tasks ← [Task('Write docs', 'low'), Task('Fix bug', 'high'), Task('Review code', 'medium'), Task('Deploy', 'high')]

    222tasks→ [Task('Write docs', 'low'), Task('Fix bug', 'high'), Task('Review code', 'medium'), Task('Deploy', 'high')] = [223    Task("Write docs", "low"),224    Task("Fix bug", "high"),225    Task("Review code", "medium"),226    Task("Deploy", "high")227]228229print("Tasks:")230for t in tasks:
    outputTasks:
  51. for t in tasks:

    pass 1 of 4
    229print("Tasks:")230for tTask('Write docs', 'low') in tasks[Task('Write docs', 'low'), Task('Fix bug', 'high'), Task('Review code', 'medium'), Task('Deploy', 'high')]:231    print(f"  {tTask('Write docs', 'low')}")
    output  Task('Write docs', 'low')
    All 4 passes — pass 1 is the card above
    passt
    1Task('Write docs', 'low')
    2Task('Fix bug', 'high')
    3Task('Review code', 'medium')
    4Task('Deploy', 'high')
  52. print(" Sorted by priority:")

    233print("\nSorted by priority:")234for t in sorted(tasks):
    output
    Sorted by priority:
  53. def __lt__(self, other):

    pass 1 of 6
    215def __lt__(selfTask('Fix bug', 'high'), otherTask('Write docs', 'low')):216    return self.PRIORITIES[self.priority]1 < \217           self.PRIORITIES[other.priority]3
    All 6 passes — pass 1 is the card above
    passselfotherself.PRIORITIES[self.priority]self.PRIORITIES[other.priority]
    1Task('Fix bug', 'high')Task('Write docs', 'low')13
    2Task('Review code', 'medium')Task('Fix bug', 'high')21
    3Task('Review code', 'medium')Task('Write docs', 'low')23
    4Task('Review code', 'medium')Task('Fix bug', 'high')21
    5Task('Deploy', 'high')Task('Review code', 'medium')12
    6Task('Deploy', 'high')Task('Fix bug', 'high')11
  54. for t in sorted(tasks):

    pass 1 of 4
    233print("\nSorted by priority:")234for tTask('Fix bug', 'high') in sorted(tasks[Task('Write docs', 'low'), Task('Fix bug', 'high'), Task('Review code', 'medium'), Task('Deploy', 'high')]):235    print(f"  {tTask('Fix bug', 'high')}")
    output  Task('Fix bug', 'high')
    All 4 passes — pass 1 is the card above
    passt
    1Task('Fix bug', 'high')
    2Task('Deploy', 'high')
    3Task('Review code', 'medium')
    4Task('Write docs', 'low')

Exercise: practical.py

Implement a cached recursive function and create partial functions for common operations