Декораторы в Python

Сегодня мы рассмотрим одну из самых мощных и элегантных возможностей Python — декораторы. Они позволяют модифицировать или расширять функции и методы без изменения их исходного кода! Представьте декораторы как специальные обертки, добавляющие дополнительную функциональность вашему коду. 🎁

Что такое декораторы?

Декоратор — это особый вид функции, которая принимает другую функцию в качестве входных данных, расширяет или изменяет ее поведение и возвращает измененную функцию без изменения ее исходного кода.

Декораторы следуют ключевому принципу в программировании — Принципу открытости/закрытости, который гласит, что код должен быть открыт для расширения, но закрыт для модификации. С помощью декораторов вы можете добавлять новое поведение функциям без изменения их оригинального кода.

Базовый синтаксис декораторов

Базовый синтаксис использования декоратора очень прост — вы размещаете строку @имя_декоратора прямо над определением функции:

Python 3.13
>>> def my_decorator(func):
...     def wrapper():
...         print("Что-то происходит до вызова функции")
...         func()  # Вызов оригинальной функции
...         print("Что-то происходит после вызова функции")
...     return wrapper

>>> @my_decorator
>>> def say_hello():
...     print("Привет, мир!")

# Вызов декорированной функции
>>> say_hello()
Что-то происходит до вызова функции
Привет, мир!
Что-то происходит после вызова функции

Это эквивалентно:

Python 3.13
>>> def my_decorator(func):
...     def wrapper():
...         print("Что-то происходит до вызова функции")
...         func()
...         print("Что-то происходит после вызова функции")
...     return wrapper

>>> def say_hello():
...     print("Привет, мир!")

# Декорирование функции вручную
>>> decorated_hello = my_decorator(say_hello)

# Вызов декорированной функции
>>> decorated_hello()
Что-то происходит до вызова функции
Привет, мир!
Что-то происходит после вызова функции

Декораторы с аргументами

Что если функция, которую вы декорируете, принимает аргументы? Вам нужно сделать вашу функцию-обертку способной принимать и передавать эти аргументы:

Python 3.13
>>> def my_decorator(func):
...     def wrapper(*args, **kwargs):
...         print("Что-то происходит до вызова функции")
...         # Вызов оригинальной функции со всеми аргументами
...         result = func(*args, **kwargs)
...         print("Что-то происходит после вызова функции")
...         return result
...     return wrapper

>>> @my_decorator
>>> def add(a, b):
...     return a + b

# Вызов декорированной функции с аргументами
>>> result = add(5, 3)
>>> print(f"Результат: {result}")
Что-то происходит до вызова функции
Что-то происходит после вызова функции
Результат: 8

Синтаксис *args и **kwargs позволяет обертке принимать любое количество позиционных и именованных аргументов, делая ее гибкой и работающей с любой функцией.

Практические примеры декораторов

Давайте рассмотрим практический пример декоратора, чтобы увидеть, насколько они могут быть полезны в реальном коде.

Замер времени выполнения функций

Декоратор для измерения времени выполнения функции:

Python 3.13
>>> import time

>>> def timing_decorator(func):
...     def wrapper(*args, **kwargs):
...         start_time = time.time()
...         result = func(*args, **kwargs)
...         end_time = time.time()
...         execution_time = end_time - start_time
...         print(f"Функция {func.__name__} выполнялась {execution_time:.4f} секунд")
...         return result
...     return wrapper

>>> @timing_decorator
>>> def calculate_sum(n):
...     return sum(range(n))

>>> calculate_sum(1000000)
Функция calculate_sum выполнялась 0.0462 секунд

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

Иногда вам нужны декораторы, которые могут принимать собственные параметры. Это требует добавления еще одного уровня вложенности:

Python 3.13
>>> def repeat(n=1):
...     def decorator(func):
...         def wrapper(*args, **kwargs):
...             result = None
...             for _ in range(n):
...                 result = func(*args, **kwargs)
...             return result
...         return wrapper
...     return decorator

>>> @repeat(n=3)
>>> def say_hi(name):
...     print(f"Привет, {name}!")
...     return "Готово"

>>> say_hi("Алиса")
Привет, Алиса!
Привет, Алиса!
Привет, Алиса!

В этом примере у нас есть три уровня функций:

  1. repeat(n) — принимает параметр декоратора
  2. decorator(func) — принимает функцию для декорирования
  3. wrapper(*args, **kwargs) — обрабатывает вызов функции

Цепочка декораторов

Вы можете применить несколько декораторов к одной функции. Они выполняются снизу вверх (декоратор, находящийся ближе всего к функции, применяется первым):

Python 3.13
>>> def bold(func):
...     def wrapper(*args, **kwargs):
...         return f"<b>{func(*args, **kwargs)}</b>"
...     return wrapper

>>> def italic(func):
...     def wrapper(*args, **kwargs):
...         return f"<i>{func(*args, **kwargs)}</i>"
...     return wrapper

>>> @bold
>>> @italic
>>> def format_text(text):
...     return text

>>> print(format_text("Привет, мир!"))
<b><i>Привет, мир!</i></b>

Декораторы классов

Помимо декорирования функций, вы также можете создавать декораторы для классов:

Python 3.13
>>> def add_greeting(cls):
...     # Добавление нового метода к классу
...     cls.greet = lambda self: f"Привет от {self.__class__.__name__}"
...     return cls

>>> @add_greeting
>>> class Person:
...     def __init__(self, name):
...         self.name = name

# Создание экземпляра и использование добавленного метода
>>> person = Person("Алиса")
>>> print(person.greet())
Привет от Person

Реальные примеры использования декораторов

Декораторы широко используются в Python и во многих популярных фреймворках:

  1. Маршрутизация во Flask/Django — сопоставление URL с функциями представления

    Python 3.13
    @app.route('/home')
    def home():
        return "Добро пожаловать на домашнюю страницу!"
    
  2. Декораторы свойств — контроль доступа к атрибутам

    Python 3.13
    class Person:
        def __init__(self, name):
            self._name = name
    
        @property
        def name(self):
            return self._name
    
        @name.setter
        def name(self, value):
            if not value:
                raise ValueError("Имя не может быть пустым")
            self._name = value
    
  3. Мемоизация/кэширование — сохранение результатов функции для избежания повторных вычислений

    Python 3.13
    def memoize(func):
        cache = {}
        def wrapper(*args):
            if args not in cache:
                cache[args] = func(*args)
            return cache[args]
        return wrapper
    
  4. Ограничение частоты — ограничение частоты вызова функции

  5. Валидация — проверка входных данных перед обработкой

  6. Авторизация — проверка разрешений перед выполнением функций

Проверка понимания

В чем основное назначение декораторов в Python?

Заключение

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

По мере продолжения вашего пути в Python, вы обнаружите, что декораторы являются важным инструментом для написания чистого, поддерживаемого и расширяемого кода. Они особенно распространены в фреймворках и библиотеках Python, поэтому понимание их работы сделает вас более эффективным в использовании этих инструментов.


Мы с вами на связи
Русский