Python ships with decorators spread across the builtins and several standard library modules. Knowing what is available eliminates the need to reinvent patterns that the language already provides. This reference catalogs every decorator in the builtins and the core modules -- functools, dataclasses, contextlib, abc, typing, and atexit -- with a concise code example and a one-paragraph explanation for each.
Built-in Decorators (No Import Required)
Three decorators are available as builtins. They require no import and are used directly in class bodies.
@property
Converts a method into a managed attribute. The method becomes a getter, and the returned property object provides .setter and .deleter methods that can be used as decorators to define write and delete behavior:
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
@property
def fahrenheit(self):
return self._celsius * 9 / 5 + 32
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("Below absolute zero")
self._celsius = value
t = Temperature(100)
print(t.fahrenheit) # 212.0
t.celsius = 0
print(t.fahrenheit) # 32.0
@staticmethod
Removes the implicit first argument (self or cls) from a method. The method behaves like a plain function namespaced inside the class:
class MathHelper:
@staticmethod
def clamp(value, low, high):
return max(low, min(value, high))
print(MathHelper.clamp(15, 0, 10)) # 10
@classmethod
Passes the class (cls) as the first argument instead of the instance. Commonly used for factory methods that produce instances through alternative constructors:
import json
class Config:
def __init__(self, settings):
self.settings = settings
@classmethod
def from_json(cls, path):
with open(path) as f:
return cls(json.load(f))
@classmethod
def defaults(cls):
return cls({"debug": False, "log_level": "INFO"})
functools Decorators
The functools module contains the highest concentration of decorators in the standard library. All require from functools import ....
@functools.wraps
Preserves the original function's metadata when writing a decorator. Copies __name__, __doc__, __module__, __qualname__, and __annotations__ from the wrapped function onto the wrapper:
from functools import wraps
def log(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
@log
def greet(name):
"""Return a greeting."""
return f"Hello, {name}"
print(greet.__name__) # greet (not 'wrapper')
print(greet.__doc__) # Return a greeting.
@functools.lru_cache
Memoizes function results using a Least Recently Used cache with a configurable maxsize (default 128). Supports optional parentheses since Python 3.8:
from functools import lru_cache
@lru_cache(maxsize=256)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(100))
print(fibonacci.cache_info())
# CacheInfo(hits=98, misses=101, maxsize=256, currsize=101)
@functools.cache
Added in Python 3.9. An unbounded version of @lru_cache with no size limit and no LRU eviction. Equivalent to @lru_cache(maxsize=None) but slightly faster because it skips the eviction bookkeeping:
from functools import cache
@cache
def factorial(n):
return n * factorial(n - 1) if n else 1
print(factorial(10)) # 3628800
@functools.cached_property
Added in Python 3.8. A property that is computed once and cached as an instance attribute. Subsequent reads return the cached value without re-executing the method. The cache can be cleared by deleting the attribute:
from functools import cached_property
class DataSet:
def __init__(self, values):
self._values = values
@cached_property
def stats(self):
print("Computing stats...")
return {
"mean": sum(self._values) / len(self._values),
"count": len(self._values),
}
ds = DataSet([10, 20, 30])
print(ds.stats) # Computing stats... {'mean': 20.0, 'count': 3}
print(ds.stats) # {'mean': 20.0, 'count': 3} (no recomputation)
@functools.singledispatch
Added in Python 3.4. Turns a function into a generic function that dispatches on the type of its first argument. Use .register to define type-specific implementations:
from functools import singledispatch
@singledispatch
def serialize(obj):
raise TypeError(f"Cannot serialize {type(obj)}")
@serialize.register(int)
def _(obj):
return str(obj)
@serialize.register(list)
def _(obj):
return "[" + ", ".join(serialize(item) for item in obj) + "]"
print(serialize(42)) # 42
print(serialize([1, 2, 3])) # [1, 2, 3]
@functools.singledispatchmethod
Added in Python 3.8. The method-compatible version of @singledispatch. Dispatches on the type of the first argument after self or cls:
from functools import singledispatchmethod
class Formatter:
@singledispatchmethod
def format(self, arg):
return str(arg)
@format.register
def _(self, arg: int):
return f"{arg:,}"
@format.register
def _(self, arg: float):
return f"{arg:.2f}"
f = Formatter()
print(f.format(1000000)) # 1,000,000
print(f.format(3.14159)) # 3.14
@functools.total_ordering
Class decorator that generates missing comparison methods from a minimal set. You define __eq__ and one of __lt__, __le__, __gt__, or __ge__; the decorator fills in the rest:
from functools import total_ordering
@total_ordering
class Version:
def __init__(self, major, minor):
self.major = major
self.minor = minor
def __eq__(self, other):
return (self.major, self.minor) == (other.major, other.minor)
def __lt__(self, other):
return (self.major, self.minor) < (other.major, other.minor)
v1, v2 = Version(1, 2), Version(2, 0)
print(v1 < v2) # True
print(v1 >= v2) # False (auto-generated)
dataclasses Decorators
@dataclasses.dataclass
Class decorator that auto-generates __init__, __repr__, __eq__, and optionally __hash__, __lt__, and others based on class annotations. Supports optional parentheses:
from dataclasses import dataclass
@dataclass(frozen=True, slots=True)
class Point:
x: float
y: float
p = Point(3.0, 4.0)
print(p) # Point(x=3.0, y=4.0)
print(p == Point(3.0, 4.0)) # True
contextlib Decorators
@contextlib.contextmanager
Converts a generator function into a context manager. Code before yield runs on entry; code after yield runs on exit:
from contextlib import contextmanager
import time
@contextmanager
def timer(label):
start = time.perf_counter()
try:
yield
finally:
print(f"{label}: {time.perf_counter() - start:.4f}s")
with timer("sort"):
sorted(range(1_000_000, 0, -1))
@contextlib.asynccontextmanager
Added in Python 3.7. The async version of @contextmanager for use with async with statements:
from contextlib import asynccontextmanager
@asynccontextmanager
async def managed_connection(url):
conn = await connect(url)
try:
yield conn
finally:
await conn.close()
abc Decorators
@abc.abstractmethod
Marks a method as abstract, requiring subclasses to provide an implementation before they can be instantiated. Can be stacked with @classmethod, @staticmethod, or @property (always as the innermost decorator):
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
...
@classmethod
@abstractmethod
def from_string(cls, s):
...
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
@classmethod
def from_string(cls, s):
return cls(float(s))
c = Circle.from_string("5")
print(c.area()) # 78.53975
typing Decorators
Decorators in the typing module are primarily consumed by type checkers and have minimal or no runtime effect.
@typing.overload
Declares multiple type signatures for a single function. The overloaded signatures are for type checkers only; the actual implementation follows without the @overload decorator:
from typing import overload
@overload
def process(data: str) -> str: ...
@overload
def process(data: int) -> int: ...
def process(data):
if isinstance(data, str):
return data.upper()
return data * 2
print(process("hello")) # HELLO
print(process(5)) # 10
@typing.final
Added in Python 3.8. Signals to type checkers that a method cannot be overridden or a class cannot be subclassed:
from typing import final
class Base:
@final
def critical_method(self):
return "do not override"
# Type checker error: Cannot override final method
# class Child(Base):
# def critical_method(self):
# return "overridden"
@typing.override
Added in Python 3.12. Marks a method as intentionally overriding a parent method. Type checkers will flag it as an error if the parent does not define that method, catching typos in method names:
from typing import override
class Animal:
def speak(self):
return "..."
class Dog(Animal):
@override
def speak(self):
return "Woof"
# Type checker error: no 'spek' in parent
# @override
# def spek(self):
# return "typo"
@typing.runtime_checkable
Added in Python 3.8. Makes a Protocol class usable with isinstance() checks at runtime:
from typing import Protocol, runtime_checkable
@runtime_checkable
class Closable(Protocol):
def close(self) -> None: ...
class Connection:
def close(self):
print("closed")
conn = Connection()
print(isinstance(conn, Closable)) # True
@typing.no_type_check
Disables type checking for a function or class. Annotations on the decorated function or class will not be evaluated by type checkers:
from typing import no_type_check
@no_type_check
def legacy_function(x, y):
# Type checker ignores this entirely
return x + y
@typing.dataclass_transform
Added in Python 3.11. Signals to type checkers that a decorator, base class, or metaclass provides dataclass-like behavior. Used by libraries like attrs, pydantic, and SQLAlchemy to get type checker support without using @dataclass directly:
from typing import dataclass_transform
@dataclass_transform()
def my_dataclass(cls):
# Library implementation that synthesizes __init__, __eq__, etc.
...
return cls
Quick Reference Table
| Decorator | Module | Added | Purpose |
|---|---|---|---|
@property | builtins | 2.2 | Managed attribute with getter/setter/deleter |
@staticmethod | builtins | 2.2 | Method with no implicit first argument |
@classmethod | builtins | 2.2 | Method receiving class as first argument |
@wraps | functools | 2.5 | Preserve wrapped function metadata |
@lru_cache | functools | 3.2 | Memoization with LRU eviction |
@total_ordering | functools | 3.2 | Auto-generate comparison methods |
@singledispatch | functools | 3.4 | Type-based function overloading |
@cached_property | functools | 3.8 | Lazily computed cached attribute |
@singledispatchmethod | functools | 3.8 | Type-based method overloading |
@cache | functools | 3.9 | Unbounded memoization |
@dataclass | dataclasses | 3.7 | Auto-generate class boilerplate |
@contextmanager | contextlib | 2.5 | Generator-based context manager |
@asynccontextmanager | contextlib | 3.7 | Async generator context manager |
@abstractmethod | abc | 2.6 | Require subclass implementation |
@overload | typing | 3.5 | Multiple type signatures (type checker only) |
@final | typing | 3.8 | Prevent override/subclass (type checker only) |
@runtime_checkable | typing | 3.8 | Enable isinstance() on Protocol |
@no_type_check | typing | 3.5 | Disable type checking |
@dataclass_transform | typing | 3.11 | Signal dataclass-like behavior |
@override | typing | 3.12 | Mark intentional method override |
Python 3.13 added @warnings.deprecated (PEP 702) for marking functions, methods, and classes as deprecated with a runtime warning and type-checker support. If you are targeting Python 3.12 or earlier, the same decorator is available via the typing_extensions package.
- The three builtins --
@property,@staticmethod,@classmethod-- are the foundation. They are implemented as descriptors and require no imports. Understanding how they customize attribute access through__get__is the key to understanding how all Python decorators interact with classes. functoolsis the decorator powerhouse. It provides caching (@cache,@lru_cache,@cached_property), dispatch (@singledispatch,@singledispatchmethod), comparison generation (@total_ordering), and the essential@wrapsfor building your own decorators correctly.- The
typingdecorators are for static analysis, not runtime behavior.@overload,@final,@override, and@dataclass_transformhave minimal or no runtime effect. They exist to give type checkers (mypy, pyright) the information needed to validate your code statically. - Always apply
@abstractmethodas the innermost decorator when stacking. The abc module documentation specifies this explicitly. When combining with@classmethod,@staticmethod, or@property, the order is:@classmethod(outer), then@abstractmethod(inner).
This reference covers every decorator shipped with CPython's standard library as of Python 3.14. Third-party libraries add their own decorators (Flask's @app.route, pytest's @pytest.fixture, Django's @login_required), but those are built on the same mechanism described throughout this series. The standard library decorators are the toolkit you should know before reaching for any external dependency.