# Декораторы

> Синтаксис Python @decorator для обёртывания функций и классов другими функциями — добавляет поведение (кэширование, логирование, проверку доступа, регистрацию) без изменения исходного кода.

Источник: https://llmdocs.ru/wiki/decorators/ · обновлено 2026-11-09 · лицензия CC BY-SA 4.0

- **Синтаксис**: @имя над def или class
- **Введены**: PEP 318 (Python 2.4, 2004)
- **Декораторы классов**: PEP 3129 (Python 3.0)
- **Произвольные выражения**: PEP 614 (Python 3.9)
- **Типизация**: ParamSpec (PEP 612)
- **Встроенные**: property, staticmethod, classmethod, functools.cache, dataclass
**Декоратор** в [Python](https://llmdocs.ru/wiki/python.md) — функция, которая принимает другую функцию (или класс) и возвращает её изменённую или обёрнутую версию. Синтаксис `@decorator` над определением функции — сокращение для присваивания `func = decorator(func)`. Декораторы позволяют добавлять общее поведение — кэширование, логирование, замер времени, проверку прав, повторные попытки, регистрацию обработчиков — не трогая код самой функции.

Декораторы повсюду в экосистеме Python: маршруты в [FastAPI](https://llmdocs.ru/wiki/fastapi.md) и [Flask](https://llmdocs.ru/wiki/flask.md) (`@app.get("/")`), фикстуры [pytest](https://llmdocs.ru/wiki/pytest.md) (`@pytest.fixture`), `@dataclass`, `@property`.

## История

Обёртывание функций было возможно всегда, но выглядело громоздко: `method = staticmethod(method)` после определения. PEP 318 ввёл синтаксис с символом `@` в Python 2.4 (2004);[1] выбор символа долго обсуждали, рассматривая десятки вариантов. В Python 3.0 декораторы распространили на классы (PEP 3129),[2] а с Python 3.9 после `@` можно писать любое выражение (PEP 614).[6]

## Как работает

Функции в Python — обычные объекты: их можно передавать как аргументы и возвращать из других функций. Декоратор пользуется этим и **замыканием** — вложенная функция запоминает переменные внешней:

```python
import functools, time

def timed(func):
    @functools.wraps(func)                  # сохранить имя, docstring и сигнатуру оригинала
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        try:
            return func(*args, **kwargs)
        finally:
            print(f"{func.__name__}: {time.perf_counter() - start:.3f} с")
    return wrapper

@timed                                     # то же, что load = timed(load)
def load(path):
    ...
```

`functools.wraps` копирует в обёртку имя, документацию и другие атрибуты исходной функции; без него отладка, трассировки и документация показывали бы `wrapper` вместо настоящего имени.[4]

## Декораторы с параметрами

Если декоратору нужны аргументы, добавляется ещё один уровень: внешняя функция принимает параметры и возвращает сам декоратор.

```python
def retry(times: int = 3, delay: float = 1.0, exceptions=(Exception,)):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(1, times + 1):
                try:
                    return func(*args, **kwargs)
                except exceptions:
                    if attempt == times:
                        raise
                    time.sleep(delay * 2 ** (attempt - 1))   # экспоненциальная пауза
        return wrapper
    return decorator

@retry(times=5, exceptions=(ConnectionError, TimeoutError))
def fetch(url): ...
```

Несколько декораторов применяются снизу вверх: в записи `@a` над `@b` над `def f` получается `f = a(b(f))`.

## Встроенные и стандартные декораторы

| Декоратор | Назначение |
|---|---|
| `@property` | метод, читаемый как атрибут; с `@x.setter` — управляемая запись |
| `@staticmethod`, `@classmethod` | методы без экземпляра; с классом вместо экземпляра |
| `@functools.cache`, `@functools.lru_cache(maxsize=…)` | запоминание результатов (мемоизация), см. [Кэш (вычисления)](https://llmdocs.ru/wiki/cache.md) |
| `@functools.cached_property` | свойство, вычисляемое один раз на экземпляр |
| `@functools.singledispatch` | разные реализации функции в зависимости от типа аргумента |
| `@dataclasses.dataclass` | генерация `__init__`, `__repr__`, `__eq__` по полям (см. [Dataclasses и Pydantic](https://llmdocs.ru/wiki/dataclasses-pydantic.md)) |
| `@contextlib.contextmanager` | генератор как [контекстный менеджер](https://llmdocs.ru/wiki/context-managers.md) |
| `@typing.override`, `@typing.final` | пометки для анализаторов типов |

```python
from functools import cache

@cache
def fib(n: int) -> int:
    return n if n < 2 else fib(n - 1) + fib(n - 2)

fib(200)      # мгновенно: каждое значение вычисляется один раз
```

## Декораторы классов и регистрация

Декоратор может не оборачивать функцию, а лишь зарегистрировать её и вернуть без изменений — так фреймворки собирают маршруты, команды CLI и обработчики событий:

```python
HANDLERS = {}

def on(event: str):
    def register(func):
        HANDLERS[event] = func
        return func
    return register

@on("user.created")
def send_welcome(user): ...
```

Декораторы классов получают класс целиком и могут добавлять методы или проверять его определение — как `@dataclass`.

## Типизация декораторов

Обёртка с `*args, **kwargs` скрывает сигнатуру исходной функции от анализаторов типов. `ParamSpec` (PEP 612) позволяет сохранить её:[5]

```python
from collections.abc import Callable

def logged[**P, R](func: Callable[P, R]) -> Callable[P, R]:
    @functools.wraps(func)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
        print("вызов", func.__name__)
        return func(*args, **kwargs)
    return wrapper
```

Теперь [mypy и pyright](https://llmdocs.ru/wiki/mypy-pyright.md) проверяют аргументы при вызове обёрнутой функции так же, как исходной (см. [Типизация в Python (type hints, PEP 484)](https://llmdocs.ru/wiki/type-hints.md)).

## Советы

- Всегда используйте `functools.wraps` в обёртках.
- Не прячьте в декораторах важную бизнес-логику: поведение, неочевидное из вызова, затрудняет чтение кода.
- Обёртка добавляет накладные расходы на вызов — для горячих функций в тесных циклах это заметно.
- Для асинхронных функций обёртка тоже должна быть `async def` и использовать `await`.

Определение и примеры — в глоссарии Python.[3]

## См. также

- [Генераторы и итераторы](https://llmdocs.ru/wiki/generators-iterators.md)
- [Контекстные менеджеры](https://llmdocs.ru/wiki/context-managers.md)
- [Dataclasses и Pydantic](https://llmdocs.ru/wiki/dataclasses-pydantic.md)
- [pytest](https://llmdocs.ru/wiki/pytest.md)
- [FastAPI](https://llmdocs.ru/wiki/fastapi.md)

## Источники

1. [PEP 318 — Decorators for Functions and Methods](https://peps.python.org/pep-0318/)
2. [PEP 3129 — Class Decorators](https://peps.python.org/pep-3129/)
3. [Python Glossary — decorator](https://docs.python.org/3/glossary.html#term-decorator)
4. [Python — functools](https://docs.python.org/3/library/functools.html)
5. [PEP 612 — Parameter Specification Variables](https://peps.python.org/pep-0612/)
6. [PEP 614 — Relaxing Grammar Restrictions On Decorators](https://peps.python.org/pep-0614/)
