GGistDev

Decorators in Python

Decorators wrap functions or methods to add behavior without changing call sites.

Basics (@ syntax)

A decorator is a callable that takes a function and returns a function.

def log_calls(func):
    def wrapper(*args, **kwargs):
        print(f"→ {func.__name__}{args, kwargs}")
        out = func(*args, **kwargs)
        print(f"← {func.__name__} -> {out!r}")
        return out
    return wrapper

@log_calls
def add(a, b):
    return a + b

Preserve metadata (functools.wraps)

Use @wraps to copy __name__, __doc__, annotations, etc.

from functools import wraps

def log(func):
    @wraps(func)
    def wrapper(*a, **k):
        print(func.__name__)
        return func(*a, **k)
    return wrapper

Decorators with arguments

Create a decorator factory that returns the real decorator.

def retry(times=3):
    def decorator(func):
        @wraps(func)
        def wrapper(*a, **k):
            last = None
            for _ in range(times):
                try:
                    return func(*a, **k)
                except Exception as e:
                    last = e
            raise last
        return wrapper
    return decorator

@retry(times=5)
def flaky(): ...

Stacking decorators

Applied top‑down, executed inside‑out.

@cache
@log
def compute(x): ...

Method decorators

Work the same on methods; first parameter is self for instance methods.

Common built‑ins

  • functools.lru_cache(maxsize=128) — memoization
  • functools.cache (3.9+) — unbounded cache
  • functools.singledispatch — function overloading by first arg type

Class decorators

Decorators can modify or replace classes.

def add_repr(cls):
    cls.__repr__ = lambda self: f"{cls.__name__}({self.__dict__})"
    return cls

@add_repr
class Point: ...

State and configuration

Store config on the wrapper or close over state; prefer pure wrappers where possible.

Testing and import‑time effects

Decorators run at import time. Keep side effects minimal and deterministic.

Summary

  • Use @wraps to preserve metadata
  • For parameters, write a decorator factory
  • Stack carefully; prefer small, focused decorators; lean on functools built‑ins