Python Decorators and Descriptors
Implementation-focused notes on Python function and class decorators, descriptor protocols, and common usage patterns.
Decorators
A decorator receives a function or class and returns the object that should replace it. Function decorators are useful for cross-cutting behavior such as timing, logging, caching, permission checks, and registration.
from functools import wraps
def trace(func): @wraps(func) def wrapper(*args, **kwargs): print('calling', func.__name__) return func(*args, **kwargs) return wrapperUse functools.wraps so the wrapper retains the original name, documentation, and related metadata. A decorator that accepts arguments is one more function layer: the outer function receives configuration and returns the actual decorator.
Descriptors
A descriptor is an object that defines __get__, __set__, or __delete__. When stored on a class, it controls attribute access on instances. Functions are descriptors too, which is how retrieving a function from an instance produces a bound method.
Data descriptors define __set__ or __delete__ and take precedence over instance attributes. Non-data descriptors can be shadowed by the instance dictionary. Descriptors are useful for validation, computed properties, lazy values, and reusable attribute behavior; store per-instance values on the instance rather than on the shared descriptor itself.