Привет! В предыдущих статьях мы научились создавать декораторы. Но что если нужно применить несколько декораторов к одной функции? Например, и замерить время выполнения, и записать логи. Для этого используют цепочку декораторов.
В этой статье мы разберём:
- Что такое цепочка декораторов
- Порядок выполнения декораторов
- Практические примеры
- Ошибки и как их избежать
- Что нужно знать перед началом
- Основная часть
- Что такое цепочка декораторов
- Как это работает под капотом
- Пример: логирование + таймер
- Порядок выполнения: пример
- Цепочка с декораторами с аргументами
- Комбинирование декораторов
- Отладка цепочки декораторов
- Задачи для закрепления
- Нюансы и подводные камни
- Порядок выполнения важен
- Влияние декораторов на друг друга
- Сохранение метаданных
- Частые ошибки и как их избежать
- Ошибка 1: Неправильный порядок
- Ошибка 2: Забыл @wraps
- Ошибка 3: Декораторы с аргументами
- Шпаргалка
- Заключение / Выводы
- КВИЗ
- Что дальше?
Что нужно знать перед началом
Для этого урока тебе понадобится:
- Установленный 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))Порядок выполнения:
my_functionоборачивается вdecorator2- Результат оборачивается в
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 |
Заключение / Выводы
Сегодня мы:
- ⛓️ Научились применять несколько декораторов
- 📝 Разобрали порядок выполнения
- 🔍 Написали практические примеры
- 🧩 Поняли, как комбинировать декораторы








