devnotes.

Decorators

A decorator is a function that wraps another function, adding behavior before, after, or around its execution — Python's idiomatic mechanism for reusable cross-cutting logic like logging, timing, or caching.

import functools
import time

def timer(func):
    @functools.wraps(func)          # preserves func's name/docstring on the wrapper
    def wrapper(*args, **kwargs):     # accepts ANY arguments, forwards them untouched
        start = time.time()
        result = func(*args, **kwargs)
        print(f"{func.__name__} took {time.time() - start:.4f}s")
        return result
    return wrapper

@timer                    # equivalent to: slow_function = timer(slow_function)
def slow_function():
    time.sleep(1)
    return "done"

slow_function()    # prints timing info, then returns "done"

from functools import lru_cache

@lru_cache(maxsize=None)     # caches results — repeated calls with the same args are instant
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

@decorator above a function definition is exactly equivalent to writing func = decorator(func) immediately after defining it — the @ syntax is purely a readability convenience. The wrapper function typically accepts *args, **kwargs so it can wrap any function's signature without knowing its specific parameters in advance, then forwards them unchanged to the original function.

@functools.wraps(func) is a small but important detail: without it, a decorated function's __name__ and docstring would be replaced by the wrapper's, breaking introspection tools, help(), and debugging — functools.wraps copies that metadata over automatically.

functools.lru_cache is a genuinely practical, ready-to-use decorator from the standard library — it memoizes a function's results, so calling fibonacci(30) a second time with the same argument returns instantly instead of recomputing, turning the classic exponential-time naive recursive Fibonacci into a fast, cached version with a single decorator line.

Decorators in Python: @wraps and lru_cache | DevNotes