functools

Updated March 13, 2026 · Modules
functools caching decorators higher-order-functions stdlib

The functools module provides tools for working with functions and other callable objects. Its most commonly used features are lru_cache for memoization, partial for partial function application, and wraps for writing well-behaved decorators.

Key Functions

lru_cache

Caches the results of a function based on its arguments. Subsequent calls with the same arguments return the cached result instead of recomputing.

from functools import lru_cache

@lru_cache(maxsize=128)
def fibonacci(n: int) -> int:
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(50))
# 12586269025

print(fibonacci.cache_info())
# CacheInfo(hits=48, misses=51, maxsize=128, currsize=51)

Use maxsize=None for an unbounded cache, or @cache (Python 3.9+) as a shorthand for @lru_cache(maxsize=None).

partial

Creates a new function with some arguments pre-filled.

from functools import partial

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

square = partial(power, exponent=2)
cube = partial(power, exponent=3)

print(square(5))   # 25
print(cube(3))     # 27

wraps

Copies metadata (name, docstring, module) from a wrapped function to its wrapper. Essential for writing decorators that don’t obscure the original function’s identity.

from functools import wraps

def log_calls(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log_calls
def greet(name: str) -> str:
    """Return a greeting."""
    return f"Hello, {name}"

print(greet("Alice"))
# Calling greet
# Hello, Alice

print(greet.__name__)  # "greet" (not "wrapper")
print(greet.__doc__)   # "Return a greeting."

Without @wraps, greet.__name__ would be "wrapper" and the docstring would be lost.

reduce

Applies a two-argument function cumulatively to the items of an iterable, reducing it to a single value.

from functools import reduce

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

total = reduce(lambda a, b: a + b, numbers)
print(total)  # 15

product = reduce(lambda a, b: a * b, numbers)
print(product)  # 120

# With an initial value
total_with_start = reduce(lambda a, b: a + b, numbers, 100)
print(total_with_start)  # 115

cached_property

A decorator that converts a method into a property that is computed once and then cached as a normal attribute. Available since Python 3.8.

from functools import cached_property

class Dataset:
    def __init__(self, values: list[float]):
        self.values = values

    @cached_property
    def mean(self) -> float:
        print("Computing mean...")
        return sum(self.values) / len(self.values)

data = Dataset([1.0, 2.0, 3.0, 4.0, 5.0])
print(data.mean)  # Computing mean... \n 3.0
print(data.mean)  # 3.0 (cached, no recomputation)

Common Patterns

Memoizing expensive computations

from functools import lru_cache

@lru_cache(maxsize=256)
def expensive_query(user_id: int, date: str) -> dict:
    # Simulate a slow database query
    return {"user_id": user_id, "date": date, "data": "..."}

Creating callback variants with partial

from functools import partial

def on_event(event_type, data, *, log=False):
    if log:
        print(f"[{event_type}] {data}")

on_click = partial(on_event, "click", log=True)
on_click("button_submit")
# [click] button_submit

Composing decorators correctly

from functools import wraps

def require_auth(func):
    @wraps(func)
    def wrapper(request, *args, **kwargs):
        if not request.get("user"):
            raise PermissionError("Not authenticated")
        return func(request, *args, **kwargs)
    return wrapper

See Also