๐Ÿ”งfunctools: partial, reduce, lru_cache, wrapsLESSON

functools: Higher-Order Functions & Caching

The functools module is Python's toolbox for working with functions as first-class objects. It provides utilities for caching expensive computations, creating specialized versions of functions, and writing well-behaved decorators.

functools.partial โ€” Partial Application

partial(func, *args, **kwargs) creates a new function with some arguments pre-filled. This is called partial application โ€” you "partially" call a function, locking in some of its arguments.

Why Use partial Instead of a Lambda?

partial also works great for callback-based APIs where you need to pass a function with no arguments but want to encode some configuration into it.

functools.reduce โ€” Fold Over a Sequence

reduce(function, iterable, initializer=None) applies a two-argument function cumulatively to items of an iterable, reducing it to a single value.

Note: For simple reductions, Python's built-ins (sum(), max(), min()) are preferred. reduce() shines for custom accumulation logic.

functools.lru_cache โ€” Memoization Made Easy

lru_cache (Least Recently Used cache) caches the return value of a function based on its arguments. When the same arguments are passed again, it returns the cached result without re-executing the function.

maxsize Parameter

maxsize controls how many different argument combinations to remember:

Important: Arguments Must Be Hashable

lru_cache uses the arguments as a dictionary key, so they must be hashable. Lists, dicts, and sets cannot be cached directly.

functools.cache โ€” Simpler Unlimited Cache (Python 3.9+)

functools.cache is shorthand for lru_cache(maxsize=None). It's simpler and slightly faster because it skips the LRU bookkeeping.

Use cache when you want unlimited caching. Use lru_cache(maxsize=N) when memory is a concern and you want to limit the cache size.

functools.wraps โ€” Preserving Decorator Metadata

When you write a decorator, your wrapper function replaces the original. This breaks __name__, __doc__, and other metadata. wraps fixes that.

wraps copies __name__, __qualname__, __doc__, __dict__, __module__, and __annotations__ from the original function to the wrapper. This makes introspection tools, documentation generators, and debuggers work correctly.

Accessing the Wrapped Function

wraps also sets __wrapped__, which points to the original function:

Combining functools Tools

The key insight is that functools transforms functions into more powerful versions of themselves โ€” adding caching, parameter locking, or metadata preservation โ€” without changing their fundamental behavior.

Knowledge Check

What does `functools.partial(pow, 2)` create?

Why must arguments to an `lru_cache`-decorated function be hashable?

What problem does `@functools.wraps(func)` solve in decorators?