Skip to content

Commit 1e4e459

Browse files
aygp-drClaude
and
Claude
committed
Implement functools module examples and tests
Add comprehensive examples of functools capabilities including: - partial function specialization - lru_cache for memoization - reduce for cumulative operations - singledispatch for function overloading - total_ordering for comparison operations - wraps for preserving function metadata Closes #5 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f79ab68 commit 1e4e459

File tree

2 files changed

+426
-2
lines changed

2 files changed

+426
-2
lines changed

functools_module.py

+222-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,223 @@
1-
# Functools Module Exercise
1+
"""
2+
Functools Module Examples
23
3-
# TODO: Complete the exercise
4+
This module demonstrates various utilities from the functools module including
5+
partial, lru_cache, reduce, singledispatch, and total_ordering.
6+
"""
7+
import time
8+
from functools import lru_cache, partial, reduce, singledispatch, total_ordering, wraps
9+
from typing import Any, Callable, Dict, List, TypeVar, cast
10+
11+
# ----- partial: Creating specialized functions -----
12+
13+
14+
def multiply(x: float, y: float) -> float:
15+
"""Multiply two numbers."""
16+
return x * y
17+
18+
19+
# Create specialized functions using partial
20+
double = partial(multiply, 2)
21+
triple = partial(multiply, 3)
22+
23+
24+
# Example with keyword arguments
25+
def format_text(text: str, prefix: str = "", suffix: str = "") -> str:
26+
"""Format text with optional prefix and suffix."""
27+
return f"{prefix}{text}{suffix}"
28+
29+
30+
# Create a function that wraps text in brackets
31+
bracket_text = partial(format_text, prefix="[", suffix="]")
32+
33+
# ----- lru_cache: Memoization for performance -----
34+
35+
36+
@lru_cache(maxsize=128)
37+
def fibonacci(n: int) -> int:
38+
"""Calculate Fibonacci numbers with caching."""
39+
if n <= 1:
40+
return n
41+
return fibonacci(n - 1) + fibonacci(n - 2)
42+
43+
44+
def fibonacci_no_cache(n: int) -> int:
45+
"""Calculate Fibonacci numbers without caching (for comparison)."""
46+
if n <= 1:
47+
return n
48+
return fibonacci_no_cache(n - 1) + fibonacci_no_cache(n - 2)
49+
50+
51+
def demonstrate_lru_cache() -> None:
52+
"""Show the performance benefits of lru_cache."""
53+
n = 35
54+
55+
# Measure time with caching
56+
start = time.time()
57+
result1 = fibonacci(n)
58+
cached_time = time.time() - start
59+
60+
# Clear the cache
61+
fibonacci.cache_clear()
62+
63+
# Measure first-time call with empty cache
64+
start = time.time()
65+
fibonacci(n) # Store result without unused variable
66+
first_time = time.time() - start
67+
68+
# Measure time without caching
69+
start = time.time()
70+
fibonacci_no_cache(n - 10) # Use smaller n to avoid excessive time
71+
no_cache_time = time.time() - start
72+
73+
print(f"Fibonacci({n}) = {result1}")
74+
print(f"Time with warmed cache: {cached_time:.6f} seconds")
75+
print(f"Time with cold cache: {first_time:.6f} seconds")
76+
print(f"Time without cache (n={n-10}): {no_cache_time:.6f} seconds")
77+
78+
79+
# ----- reduce: Cumulative operations -----
80+
81+
82+
def sum_numbers(numbers: List[int]) -> int:
83+
"""Sum a list of numbers using reduce."""
84+
return reduce(lambda a, b: a + b, numbers, 0)
85+
86+
87+
def factorial(n: int) -> int:
88+
"""Calculate factorial using reduce."""
89+
return reduce(lambda a, b: a * b, range(1, n + 1), 1)
90+
91+
92+
def concatenate_strings(strings: List[str], separator: str = "") -> str:
93+
"""Concatenate a list of strings using reduce."""
94+
if not strings:
95+
return ""
96+
return reduce(lambda a, b: f"{a}{separator}{b}", strings)
97+
98+
99+
# ----- singledispatch: Function overloading based on type -----
100+
101+
102+
@singledispatch
103+
def process_data(data: Any) -> str:
104+
"""Process data based on its type."""
105+
return f"Unknown type: {type(data).__name__}"
106+
107+
108+
@process_data.register
109+
def _(data: str) -> str:
110+
return f"Processing string: {data.upper()}"
111+
112+
113+
@process_data.register
114+
def _(data: int) -> str:
115+
return f"Processing integer: {data * 2}"
116+
117+
118+
@process_data.register
119+
def _(data: list) -> str:
120+
return f"Processing list with {len(data)} items"
121+
122+
123+
@process_data.register(dict) # Alternative registration syntax
124+
def _(data: Dict[Any, Any]) -> str:
125+
return f"Processing dictionary with {len(data)} keys"
126+
127+
128+
# ----- total_ordering: Complete comparison operations -----
129+
130+
131+
@total_ordering
132+
class Person:
133+
"""Person class with comparison operations defined."""
134+
135+
def __init__(self, name: str, age: int) -> None:
136+
self.name = name
137+
self.age = age
138+
139+
def __eq__(self, other: Any) -> bool:
140+
if not isinstance(other, Person):
141+
return NotImplemented
142+
return self.age == other.age
143+
144+
def __lt__(self, other: Any) -> bool:
145+
if not isinstance(other, Person):
146+
return NotImplemented
147+
return self.age < other.age
148+
149+
def __repr__(self) -> str:
150+
return f"Person(name='{self.name}', age={self.age})"
151+
152+
153+
# ----- wraps: Preserve metadata in decorators -----
154+
155+
T = TypeVar("T", bound=Callable[..., Any])
156+
157+
158+
def debug(func: T) -> T:
159+
"""Decorator to print function name and arguments."""
160+
161+
@wraps(func)
162+
def wrapper(*args: Any, **kwargs: Any) -> Any:
163+
"""Wrapper preserving original function metadata."""
164+
print(f"Calling {func.__name__} with args: {args}, kwargs: {kwargs}")
165+
result = func(*args, **kwargs)
166+
print(f"{func.__name__} returned: {result}")
167+
return result
168+
169+
return cast(T, wrapper)
170+
171+
172+
@debug
173+
def greet(name: str) -> str:
174+
"""Function with a docstring that should be preserved."""
175+
return f"Hello, {name}!"
176+
177+
178+
# Example usage
179+
if __name__ == "__main__":
180+
# partial examples
181+
print("\n--- partial examples ---")
182+
print(f"double(5) = {double(5)}")
183+
print(f"triple(5) = {triple(5)}")
184+
print(f"bracket_text('example') = {bracket_text('example')}")
185+
186+
# lru_cache examples
187+
print("\n--- lru_cache examples ---")
188+
demonstrate_lru_cache()
189+
190+
# reduce examples
191+
print("\n--- reduce examples ---")
192+
print(f"sum_numbers([1, 2, 3, 4, 5]) = {sum_numbers([1, 2, 3, 4, 5])}")
193+
print(f"factorial(5) = {factorial(5)}")
194+
print(
195+
f"concatenate_strings(['Hello', 'World']) = {concatenate_strings(['Hello', 'World'])}"
196+
)
197+
print(
198+
f"concatenate_strings(['a', 'b', 'c'], '-') = {concatenate_strings(['a', 'b', 'c'], '-')}"
199+
)
200+
201+
# singledispatch examples
202+
print("\n--- singledispatch examples ---")
203+
print(process_data("hello"))
204+
print(process_data(42))
205+
print(process_data([1, 2, 3]))
206+
print(process_data({"a": 1, "b": 2}))
207+
print(process_data(3.14)) # Uses default implementation
208+
209+
# total_ordering examples
210+
print("\n--- total_ordering examples ---")
211+
alice = Person("Alice", 30)
212+
bob = Person("Bob", 25)
213+
charlie = Person("Charlie", 30)
214+
215+
print(f"{alice} > {bob}: {alice > bob}")
216+
print(f"{alice} == {charlie}: {alice == charlie}")
217+
print(f"{bob} <= {charlie}: {bob <= charlie}")
218+
219+
# wraps examples
220+
print("\n--- wraps examples ---")
221+
print(f"greet.__name__: {greet.__name__}")
222+
print(f"greet.__doc__: {greet.__doc__}")
223+
result = greet("Claude")

0 commit comments

Comments
 (0)