Python декораторы: простое объяснение с примерами и кодом
-
Декораторы в Python — это мощный инструмент для расширения функций без изменения их кода. Они позволяют добавлять логику вроде логирования, кэширования или проверки доступа, делая код чище и повторно используемым.
Зачем они нужны? Представь, что у тебя есть функция для вычисления, и ты хочешь каждый раз измерять её время или логировать вызовы. Вместо копипасты кода везде просто пишешь декоратор и вешаешь его одной строкой. Это решает проблему дублирования и упрощает поддержку.
Что такое декоратор на самом деле
Декоратор — это функция, которая принимает другую функцию как аргумент, оборачивает её в новую логику и возвращает обновлённую версию. Всё происходит автоматически с помощью синтаксиса @имя_декоратора. Это как обёртка: внешний слой добавляет поведение, а внутри остаётся оригинальная функция.
Рассмотрим базовый пример. Допустим, у нас есть простая функция say_hi, которая возвращает строку “всем привет”. Декоратор сделает буквы заглавными, не трогая код функции. Оборачиваем функцию во внутреннюю wrapper, выполняем нужную логику до и после вызова оригинала — и готово. Такой подход работает для любых функций, даже с параметрами.
Вот как выглядит структура:
- Декоратор принимает func.
- Внутри создаёт wrapper, которая вызывает func с аргументами.
- Wrapper возвращает результат func плюс добавленная логика.
def uppercase_decorator(func): def wrapper(): result = func() return result.upper() return wrapper @uppercase_decorator def say_hi(): return "всем привет" print(say_hi()) # ВСЕМ ПРИВЕТКлючевой момент: *wrapper должна принимать *args и *kwargs, чтобы декоратор работал с любыми функциями.
Простые примеры декораторов в действии
Начнём с логирования. Представь функцию, которая печатает имя. Декоратор перед вызовом выведет “Функция вызвана”. Это полезно для отладки: видишь стек вызовов без if-ов повсюду. Аналогично можно добавить timestamp или уровень логирования.
Другой пример — замер времени. Функция вычисляет сумму списка, а декоратор покажет, сколько прошло миллисекунд. Импортируешь time, фиксируешь start и end в wrapper — и вуаля. Идеально для оптимизации кода.
Переходим к деталям. Вот типичные случаи:
- Логирование: print(f"Вызвана {func.name}")
- Время выполнения: time.perf_counter() до и после
- Проверка аргументов: raise ValueError, если не ок
Пример декоратора Что добавляет Когда использовать Логирование Сообщение о вызове Отладка, мониторинг Тайминг Время в мс Профилирование Кэширование Сохранение результата Дорогие вычисления Эти примеры показывают, как декораторы упрощают жизнь. Не забывай возвращать результат func, иначе функция ничего не отдаст.
Декораторы с параметрами и несколько штук сразу
Обычный декоратор не принимает аргументы сам по себе. Чтобы добавить параметры, создай фабрику: внешняя функция принимает настройки, внутри — настоящий декоратор с wrapper. Например, декоратор повторений: @retry(times=3) — попробует функцию 3 раза при ошибке.
С несколькими декораторами порядок важен. Они применяются снизу вверх: первый ближе к функции оборачивает её, следующий — результат предыдущего. Если @log над @time, то сначала time, потом log. Это накапливает эффекты: время + лог + валидация.
Структура декоратора с параметрами:
- Внешняя функция repeat(n):
- Возвращает decorator(func)
- Decorator возвращает wrapper с циклом на n попыток
- Wrapper ловит исключения и retry
def repeat(n): def decorator(func): def wrapper(*args, **kwargs): for _ in range(n): try: return func(*args, **kwargs) except: pass raise return wrapper return decorator @repeat(3) def risky_func(): # может упасть passВажно: сохраняй метаданные func с functools.wraps(func) — имя, докстринг останутся правильными.
Декораторы на классах и продвинутые фичи
Не только функции: классы с init и call тоже декораторы. В init сохраняешь func, в call — выполняешь логику. Полезно для состояний, как счётчик вызовов или кэш в атрибуте.
Применение: мемоизация. Декоратор хранит результаты в словаре {args: result}. При повторном вызове с теми же аргументами — берёт из кэша. Идеально для рекурсии Фибоначчи или API-запросов.
Продвинутые примеры:
- Мемоизация: @lru_cache из functools
- Состояние в классе: self.count +=1
- Для методов: self-aware wrapper
Тип декоратора Преимущества Ограничения Функция Просто Нет состояния Класс Состояние Словеснее functools Готовые Меньше контроля Классовые декораторы хороши, когда нужна память между вызовами.
Когда декораторы меняют правила игры
Декораторы — это не просто сахар: они принцип SOLID в действии. Модифицируешь поведение без изменения класса или функции, делая код модульным. В веб-фреймворках вроде Flask они проверяют роли, в тестах — мокнут зависимости.
Осталось место для размышлений: как комбинировать с async/await или генераторами? Или создать декоратор для валидации схем с pydantic. Подумай над своими проектами — наверняка найдётся, где упростить логику.
Это базовый набор, чтобы стартовать уверенно. Дальше экспериментируй с реальными задачами.
© 2024 - 2025 ExLends, Inc. Все права защищены.