import functools
@functools.lru_cache(maxsize=None)
def fib(n):
return n if n < 2 else fib(n-1) + fib(n-2)
print(fib(30)) # cached, fast
Topics
20
Async Generators & Async Context Managers
Command-Line Interfaces (argparse)
Comprehensions & Generators
Concurrency (asyncio/threading/multiprocessing)
Data Types & Structures
Dataclasses & NamedTuples
Debugging & Profiling
Decorators
Descriptors & Properties
Exception Handling
File I/O & Context Managers
Functions & Scope
functools & Functional Programming Tools
Iterators & the Iterator Protocol
Logging
Magic Methods & Operator Overloading
Memory Management & Garbage Collection
Metaclasses & Class Customization
Modules & Packaging
Multiple Inheritance & MRO
functools & Functional Programming Tools
15 questions found
functools provides higher-order functions that act on or return other functions, such as tools for caching (lru_cache, cache), partial application (partial), reducing sequences (reduce), and customizing comparisons (cmp_to_key, total_ordering). It centralizes common functional-programming utilities so you don't reimplement them.
Real-world example
A pricing service caches expensive tax-rate lookups per region using lru_cache to avoid repeated database hits.
Decorators;Closures
partial creates a new callable with some positional or keyword arguments pre-filled, returning a function with a smaller remaining signature. It's useful for adapting a general function to a specific callback interface, like event handlers or map() calls, without writing a wrapper function or lambda.
from functools import partial
def power(base, exp):
return base ** exp
square = partial(power, exp=2)
print(square(5)) # 25
Real-world example
A GUI framework binds partial(handle_click, button_id=3) as a callback so the click handler already knows which button fired.
Decorators;Functions & Scope
reduce(function, iterable, initializer) cumulatively applies a binary function to items of an iterable, reducing it to a single value. It's discouraged for complex logic because the accumulation is implicit and harder to read than an explicit loop or a built-in like sum() or math.prod(), which are clearer for common cases.
from functools import reduce
product = reduce(lambda acc, x: acc * x, [1,2,3,4], 1)
print(product) # 24
Real-world example
A financial report combines a list of transaction deltas into a running total balance using reduce, though sum() would be clearer for simple addition.
Comprehensions & Generators;Iterators & the Iterator Protocol
When a decorator replaces a function with a wrapper, the wrapper loses the original's __name__, __doc__, and other metadata. @functools.wraps(func) applied to the wrapper copies that metadata over, so introspection tools, help(), and debuggers still show the original function's identity.
import functools
def log_calls(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f'Calling {func.__name__}')
return func(*args, **kwargs)
return wrapper
@log_calls
def greet():
'''Say hello.'''
pass
print(greet.__name__, greet.__doc__)
Real-world example
A web framework's logging decorator uses wraps so Flask's URL routing still sees the correct endpoint function name instead of 'wrapper'.
Decorators;Magic Methods & Operator Overloading
singledispatch turns a function into a generic function that dispatches to type-specific implementations registered with @func.register(Type). The base function acts as the default implementation, and Python picks the most specific matching registered implementation based on the first argument's runtime type.
from functools import singledispatch
@singledispatch
def describe(obj):
return f'object: {obj}'
@describe.register
def _(obj: int):
return f'integer: {obj}'
@describe.register
def _(obj: list):
return f'list of {len(obj)} items'
print(describe(5), describe([1,2,3]))
Real-world example
A serialization library uses singledispatch to convert different Python types (int, list, datetime) to JSON-compatible representations without a long if/elif chain.
Multiple Inheritance & MRO;Decorators
What is functools.cached_property and how does it differ from @property combined with manual caching?
Advancedcached_property computes a value once on first access and stores it in the instance's __dict__ under the same attribute name, replacing itself so subsequent lookups skip the descriptor entirely and hit the cached value directly -- faster than a property that manually checks a cache flag each call. It requires the instance to support attribute assignment (no __slots__ conflict).
from functools import cached_property
class Report:
def __init__(self, data):
self.data = data
@cached_property
def summary(self):
print('Computing...')
return sum(self.data)
r = Report([1,2,3])
print(r.summary) # Computing... 6
print(r.summary) # 6 (no recompute)
Real-world example
A data model class uses cached_property to lazily compute an expensive aggregate statistic only when first requested by a report view.
Descriptors & Properties;Memory Management & Garbage Collection
total_ordering takes a class that defines __eq__ and one of __lt__, __le__, __gt__, or __ge__, then fills in the remaining comparison methods automatically. This avoids manually writing all six rich comparison methods for classes that need full ordering support.
from functools import total_ordering
@total_ordering
class Version:
def __init__(self, num):
self.num = num
def __eq__(self, other):
return self.num == other.num
def __lt__(self, other):
return self.num < other.num
print(Version(1) <= Version(2)) # True, auto-derived
Real-world example
A package manager's Version class uses total_ordering to support sorting release versions without writing __le__, __gt__, and __ge__ by hand.
Magic Methods & Operator Overloading;Data Types & Structures
functools.cache (added in Python 3.9) is a simpler, unbounded version of lru_cache -- equivalent to lru_cache(maxsize=None). Use cache when you want unlimited caching with simpler syntax, and lru_cache when you need to bound memory usage with maxsize or need typed caching.
from functools import cache
@cache
def factorial(n):
return 1 if n <= 1 else n * factorial(n-1)
print(factorial(10))
Real-world example
A configuration loader caches parsed config file contents with @cache since the file rarely changes during a program's run.
functools & Functional Programming Tools;Memory Management & Garbage Collection
Every lru_cache-wrapped function gets cache_info() (returns hits, misses, maxsize, currsize) and cache_clear() (empties the cache) methods attached. This is useful for debugging cache effectiveness or resetting state between test runs.
from functools import lru_cache
@lru_cache(maxsize=128)
def square(x):
return x * x
square(4); square(4); square(5)
print(square.cache_info()) # CacheInfo(hits=1, misses=2, maxsize=128, currsize=2)
square.cache_clear()
Real-world example
A test suite calls cache_clear() in a pytest fixture teardown to ensure cached results from one test don't leak into another.
Debugging & Profiling;functools & Functional Programming Tools
lru_cache stores a reference to every argument, including self, as part of the cache key. Applied to an instance method, this keeps instances alive as long as the cache holds entries, potentially causing memory leaks, and cache entries are shared across all instances by argument value rather than being per-instance.
from functools import lru_cache
class Service:
@lru_cache(maxsize=None)
def compute(self, x): # caches (self, x) pairs -- keeps self alive
return x * 2
Real-world example
A long-running server process notices memory growth traced to a cached instance method holding references to short-lived request objects via self.
Memory Management & Garbage Collection;Descriptors & Properties
Showing 1–10 of 15