Объяснение декораторов Python: от @property до повтора логики за 15 минут (2026 г.)
Декораторы — это функция, которая отделяет «знает Python» от «свободно читает Python». Они выглядят волшебно, но они механические: @decorator для функции точно f = декоратор(f). Вот и все. Все остальное в этом уроке сводится к тому, что одна линия применяется в разных формах.
Теория одной линии
Когда вы пишете:
Python преобразует его в:
Вот и вся механика. @decorator_name над def запускает def_name = Decorator_name(def_name) после определения. Если вы усвоите это, декораторы навсегда перестанут быть загадочными.
Написание своего первого декоратора (60 секунд)
Декоратором является любой вызываемый объект, который принимает функцию и возвращает функцию. Типичная форма:
*args, **kwargs в обертке является защитным шаблоном — он позволяет декоратору работать с любой функцией независимо от сигнатуры. Пропустите это, и вы получите TypeError: Wrapper() принимает 0 позиционных аргументов, но было дано 2 в тот момент, когда кто-то украшает функцию параметрами.
Исправление @functools.wraps
Одна тонкая ошибка: после оформления add.__name__ становится "оберткой", а не "add"`. Строки документации и подсказки типов также исчезают. Исправление - это один импорт:
Правило: каждый декоратор, который вы пишете, должен использовать `@functools.wraps(fn)`. Он стоит одну строку и предотвращает целую категорию проблем отладки (самоанализ, фикстуры pytest, форматтеры loging, pickle — без него все ломается молча).
Четыре декоратора, которые вы увидите в каждой базе кода
1. @property — вычисляемые атрибуты
@property делает метод вызываемым как если бы это был атрибут. Идеально подходит для производных значений, доступных только для чтения. Также существует @area.setter для пути записи, но 90% случаев использования @property доступны только для чтения.
2. @staticmethod и @classmethod
@classmethod получает cls (сам класс) и используется для альтернативных конструкторов. @staticmethod не имеет ничего особенного и представляет собой простую функцию, которая просто живет внутри пространства имен класса.
3. @functools.cache — мемоизация
Один из самых мощных декораторов stdlib. Автоматически запоминает функцию на основе ее аргументов. Требует, чтобы все аргументы были хешируемыми (нет списков/диктов в качестве аргументов — используйте кортежи).
4. @dataclasses.dataclass — устранение шаблона
@dataclass считывает аннотации типов уровня класса и автоматически генерирует __init__, __repr__, __eq__ и (с frozen=True) __hash__. Это самое большое улучшение качества жизни в Python 3.7+.
Написание декоратора @retry (реальный пример)
Декоратор, который повторяет функцию в случае сбоя с экспоненциальной отсрочкой:
Обратите внимание на три вложенные функции: retry принимает аргументы конфигурации и возвращает decorator; декоратор принимает функцию и возвращает обертку; wrapper запускает цикл повтора. Этот трехслойный шаблон выглядит так, как выглядит любой декоратор с аргументами — запоминайте форму, а не конкретный пример.
Декораторы на основе классов (редко, но полезно)
Декоратором может быть любой вызываемый объект. Классы с __call__ тоже работают:
Используйте, когда декоратору необходимо удерживать состояние, которое переживет один вызов (счетчик, кеш, сегмент ограничения скорости).
Распространенные ошибки
1. Забыть `@functools.wraps` — самоанализ прерывается бесшумно.
2. Изменяемые аргументы по умолчанию в самом декораторе — здесь также встречается классическая ошибка def wrapper(x,cache=[]).
3. Украшение методов без принятия `self` — помните, что self — это всего лишь позиционный аргумент; *args, **kwargs прекрасно это воспринимает.
4. При штабелировании порядок имеет значение: @a\n@b\ndef f() равен f = a(b(f)). Читайте стопки снизу вверх.
Практика
Напишите декоратор @timing, который выводит время выполнения обернутой функции, а затем примените его к функции, которая вычисляет sum(range(10_000_000)). Весь декоратор состоит из 8 строк с functools.wraps + time.perf_counter.
Как только вы усвоите, что @x — это f = x(f), декораторы станут одной из наиболее продуктивных функций Python. Они повсюду в реальных базах кода: маршруты Flask/FastAPI (@app.route), фикстуры pytest («@pytest.fixture»), представления Django («@login_required»), валидаторы Pydantic («@field_validator»). Научившись писать их самостоятельно, вы сможете читать каждую из них.
Следующий шаг: 10 практических уроков по декоратору — создайте @retry, @memoise, @rate_limit и @requires_auth с нуля, используя настоящий Python, работающий в вашем браузере.