Цепочка декораторов: применение нескольких

Цепочка декораторов: применение нескольких Декораторы

Привет! В предыдущих статьях мы научились создавать декораторы. Но что если нужно применить несколько декораторов к одной функции? Например, и замерить время выполнения, и записать логи. Для этого используют цепочку декораторов.

В этой статье мы разберём:

  • Что такое цепочка декораторов
  • Порядок выполнения декораторов
  • Практические примеры
  • Ошибки и как их избежать

Что нужно знать перед началом

Для этого урока тебе понадобится:

  • Установленный Python
  • Базовое понимание декораторов (статьи 1–4 в этом подменю)

Совет: Цепочка декораторов позволяет комбинировать поведение нескольких декораторов в одном месте.

Основная часть

Что такое цепочка декораторов

Цепочка декораторов — это применение нескольких декораторов к одной функции.

@decorator1
@decorator2
@decorator3
def my_function():
    pass

Совет: Порядок выполнения: снизу вверх. Сначала @decorator3, потом @decorator2, потом @decorator1.

Как это работает под капотом

@decorator1
@decorator2
def my_function():
    pass

# Эквивалентно
my_function = decorator1(decorator2(my_function))

Порядок выполнения:

  1. my_function оборачивается в decorator2
  2. Результат оборачивается в decorator1

Пример: логирование + таймер

import time
from functools import wraps

def logger(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Вызов функции: {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

def timer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"Время выполнения: {end - start:.4f} сек")
        return result
    return wrapper

@timer
@logger
def slow_function():
    time.sleep(1)
    return "Готово!"

slow_function()

# Вывод:
# Вызов функции: slow_function
# Время выполнения: 1.0012 сек

Совет: Сначала применяется @logger, потом @timer.

Порядок выполнения: пример

def decorator1(func):
    def wrapper(*args, **kwargs):
        print("Начало decorator1")
        result = func(*args, **kwargs)
        print("Конец decorator1")
        return result
    return wrapper

def decorator2(func):
    def wrapper(*args, **kwargs):
        print("Начало decorator2")
        result = func(*args, **kwargs)
        print("Конец decorator2")
        return result
    return wrapper

@decorator1
@decorator2
def my_function():
    print("Выполнение функции")

my_function()

# Вывод:
# Начало decorator1
# Начало decorator2
# Выполнение функции
# Конец decorator2
# Конец decorator1

Совет: decorator1 оборачивает результат decorator2.

Цепочка с декораторами с аргументами

def repeat(times):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(times):
                func(*args, **kwargs)
        return wrapper
    return decorator

@timer
@repeat(times=3)
def say_hello():
    print("Привет!")

say_hello()

# Вывод:
# Привет! (3 раза)
# Время выполнения: 0.0001 сек

Комбинирование декораторов

def require_admin(func):
    @wraps(func)
    def wrapper(user, *args, **kwargs):
        if not user.get("is_admin", False):
            print("Доступ запрещён")
            return None
        return func(user, *args, **kwargs)
    return wrapper

def log_action(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Действие: {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log_action
@require_admin
def delete_user(user, user_id):
    print(f"Пользователь {user_id} удалён")

admin = {"name": "admin", "is_admin": True}
delete_user(admin, 123)

# Вывод:
# Действие: delete_user
# Пользователь 123 удалён

Отладка цепочки декораторов

def debug_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Вызов {func.__name__} с args={args}, kwargs={kwargs}")
        result = func(*args, **kwargs)
        print(f"Результат: {result}")
        return result
    return wrapper

@timer
@debug_decorator
def add(a, b):
    return a + b

add(3, 5)

# Вывод:
# Вызов add с args=(3, 5), kwargs={}
# Результат: 8
# Время выполнения: 0.0000 сек

Задачи для закрепления

Задача 1. Напиши функцию с двумя декораторами: @timer и @logger.

Задача 2. Напиши функцию с тремя декораторами: @timer@logger@repeat(3).

Задача 3. В каком порядке выполняются декораторы? Приведи пример.

Задача 4. Что будет, если поменять местами @logger и @timer?

Задача 5. Как отладить цепочку декораторов?

Ответы:

Задача 1.

@timer
@logger
def slow_function():
    time.sleep(0.5)

Задача 2.

@timer
@logger
@repeat(3)
def say_hello():
    print("Hello")

Задача 3.

# Декораторы выполняются снизу вверх.
# @decorator1 @decorator2 => decorator2(func) -> decorator1(result)

Задача 4.

# Если поменять местами, порядок выполнения изменится.
# @logger @timer => сначала таймер, потом логгер.

Задача 5.

# Добавить debug-декоратор для отслеживания выполнения.

Нюансы и подводные камни

Порядок выполнения важен

@timer
@logger
def func():
    pass

# Результат: сначала логирование, потом таймер

Влияние декораторов на друг друга

Декораторы могут влиять друг на друга. Например, @timer может замерить время с учётом логирования.

Сохранение метаданных

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

Частые ошибки и как их избежать

Ошибка 1: Неправильный порядок

Сначала декораторы, которые должны выполняться ближе к функции (снизу), потом внешние (сверху).

Ошибка 2: Забыл @wraps

Используй @wraps в каждом декораторе.

Ошибка 3: Декораторы с аргументами

Декораторы с аргументами ставь в цепочку как обычные.

Шпаргалка

Что нужноКак пишется
Цепочка декораторов@deco1 @deco2 def func():
Порядок выполненияСнизу вверх
Комбинация декораторов@timer @logger

Заключение / Выводы

Сегодня мы:

  • ⛓️ Научились применять несколько декораторов
  • 📝 Разобрали порядок выполнения
  • 🔍 Написали практические примеры
  • 🧩 Поняли, как комбинировать декораторы

КВИЗ

Что дальше?

Оцените статью
IMI-DS - PYTHON LERNEN
Содержание
Оглавление ×