Привет!
Ты уже знаком с функциями в Python. Но что, если нужно добавить дополнительное поведение к уже существующей функции, не меняя её код? Именно для этого существуют декораторы.
Звучит сложно? На самом деле, декораторы — это просто функции, которые принимают другую функцию и возвращают её с дополнительным поведением.
В этой статье мы разберём:
- Что такое декораторы
- Зачем они нужны
- Как работают замыкания (closures)
- Простые примеры декораторов
- Что нужно знать перед началом
- Основная часть
- Что такое декоратор
- Функции высшего порядка и замыкания
- Простой декоратор (без синтаксиса @)
- Декоратор с синтаксисом @
- Декоратор для замера времени выполнения
- Декоратор для проверки прав доступа
- Декоратор с несколькими функциями
- Задачи для закрепления
- Нюансы и подводные камни
- Сохранение метаданных функции
- Декораторы без аргументов и с аргументами
- Цепочка декораторов
- Частые ошибки и как их избежать
- Ошибка 1: Забыл возвращать функцию
- Ошибка 2: Неправильное использование *args и **kwargs
- Ошибка 3: Потеря метаданных
- Шпаргалка
- Заключение / Выводы
- КВИЗ
- Что дальше?
Что нужно знать перед началом
Для этого урока тебе понадобится:
- Установленный Python
- Базовое понимание функций (создание, вызов, передача в качестве аргументов)
Совет: Декораторы — это продвинутая тема. Не расстраивайся, если не поймёшь всё сразу. С практикой придёт понимание.
Основная часть
Что такое декоратор
Декоратор — это функция, которая принимает другую функцию в качестве аргумента и возвращает новую функцию с расширенным поведением.
Простая аналогия:
Представь, что у тебя есть простая функция say_hello(). Ты хочешь, чтобы перед её выполнением всегда печаталась строка «Начинаем работу». Вместо того чтобы менять код say_hello(), ты создаёшь декоратор, который оборачивает её в дополнительную логику.
Функции высшего порядка и замыкания
Прежде чем погрузиться в декораторы, давай вспомним два важных понятия:
- Функции высшего порядка — функции, которые принимают другие функции как аргументы или возвращают их.
def greet(name):
return f"Привет, {name}!"
def run_function(func, arg):
return func(arg)
result = run_function(greet, "Анна")
print(result) # Привет, Анна!- Замыкания (closures) — функция, которая «запоминает» переменные из внешней области, даже после того как внешняя функция завершила работу.
def make_multiplier(factor):
def multiplier(x):
return x * factor
return multiplier
double = make_multiplier(2)
print(double(5)) # 10Совет: Замыкания — основа декораторов. Они позволяют декоратору «запомнить» исходную функцию.
Простой декоратор (без синтаксиса @)
Давай создадим декоратор, который добавляет логирование перед вызовом функции.
def logger(func):
def wrapper(*args, **kwargs):
print(f"Вызов функции: {func.__name__}")
result = func(*args, **kwargs)
print(f"Результат: {result}")
return result
return wrapper
# Применяем декоратор вручную
def add(a, b):
return a + b
add_with_logging = logger(add)
add_with_logging(3, 5)
# Вывод:
# Вызов функции: add
# Результат: 8Как это работает:
loggerпринимает функциюfunc- Внутри
loggerсоздаётся функцияwrapper, которая обёртывает вызовfunc wrapperдобавляет дополнительное поведение (логирование)loggerвозвращаетwrapper
Декоратор с синтаксисом @
В Python есть специальный синтаксис для применения декораторов — символ @.
def logger(func):
def wrapper(*args, **kwargs):
print(f"Вызов функции: {func.__name__}")
result = func(*args, **kwargs)
print(f"Результат: {result}")
return result
return wrapper
@logger
def add(a, b):
return a + b
add(3, 5)
# Вывод:
# Вызов функции: add
# Результат: 8Совет:
@loggerэквивалентноadd = logger(add). Просто короче и красивее.
Декоратор для замера времени выполнения
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"Функция {func.__name__} выполнилась за {end - start:.4f} секунд")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
return "Готово!"
slow_function()
# Вывод:
# Функция slow_function выполнилась за 1.0012 секундСовет: Это очень полезный декоратор для оптимизации кода.
Декоратор для проверки прав доступа
def require_admin(func):
def wrapper(user, *args, **kwargs):
if not user.get("is_admin", False):
print("Доступ запрещён: требуется права администратора")
return None
return func(user, *args, **kwargs)
return wrapper
@require_admin
def delete_user(user, user_id):
print(f"Пользователь {user_id} удалён")
admin_user = {"name": "admin", "is_admin": True}
regular_user = {"name": "alice", "is_admin": False}
delete_user(admin_user, 123) # Пользователь 123 удалён
delete_user(regular_user, 456) # Доступ запрещён: требуется права администратораДекоратор с несколькими функциями
def logger(func):
def wrapper(*args, **kwargs):
print(f"Вызов функции: {func.__name__}")
return func(*args, **kwargs)
return wrapper
@logger
def greet(name):
return f"Привет, {name}!"
@logger
def add(a, b):
return a + b
print(greet("Анна")) # Вызов функции: greet
print(add(3, 5)) # Вызов функции: addЗадачи для закрепления
Задача 1. Напиши декоратор, который выводит «Начало работы» и «Конец работы» до и после вызова функции.
Задача 2. Напиши декоратор, который замеряет время выполнения функции.
Задача 3. Напиши декоратор, который проверяет, что пользователь является администратором.
Задача 4. Напиши декоратор, который выводит аргументы функции перед её вызовом.
Задача 5. Что произойдёт, если декоратор не возвращает функцию?
Ответы:
Задача 1.
def border(func):
def wrapper(*args, **kwargs):
print("Начало работы")
result = func(*args, **kwargs)
print("Конец работы")
return result
return wrapper
@border
def say_hello():
print("Привет, мир!")
say_hello()
# Начало работы
# Привет, мир!
# Конец работыЗадача 2.
import time
def timer(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
def slow():
time.sleep(0.5)
return "Готово"
slow()Задача 3.
def require_admin(func):
def wrapper(user, *args, **kwargs):
if not user.get("is_admin", False):
return "Доступ запрещён"
return func(user, *args, **kwargs)
return wrapper
@require_admin
def delete_user(user, user_id):
return f"Пользователь {user_id} удалён"Задача 4.
def debug(func):
def wrapper(*args, **kwargs):
print(f"Аргументы: {args}, {kwargs}")
return func(*args, **kwargs)
return wrapperЗадача 5.
# Если декоратор не возвращает функцию, исходная функция будет заменена на None.Нюансы и подводные камни
Сохранение метаданных функции
Декоратор изменяет имя функции:
def logger(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@logger
def greet():
pass
print(greet.__name__) # wrapper (а не greet)Как исправить: Используй functools.wraps.
from functools import wraps
def logger(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapperДекораторы без аргументов и с аргументами
Простой декоратор без аргументов:
@logger
def func():
passДекоратор с аргументами:
@logger(level="DEBUG")
def func():
passЦепочка декораторов
@timer
@logger
def my_func():
passЧастые ошибки и как их избежать
Ошибка 1: Забыл возвращать функцию
Всегда возвращай внутреннюю функцию.
Ошибка 2: Неправильное использование *args и **kwargs
Всегда используй *args и **kwargs для поддержки любых аргументов.
Ошибка 3: Потеря метаданных
Используй functools.wraps для сохранения имени и докстринга.
Шпаргалка
| Что нужно | Как пишется |
|---|---|
| Базовый декоратор | def decorator(func): def wrapper(*args, **kwargs): ... return wrapper |
| С сохранением метаданных | from functools import wraps |
| Применение декоратора | @decorator |
| Цепочка декораторов | @deco1 @deco2 |
Заключение / Выводы
Сегодня мы:
- Узнали, что такое декораторы
- Разобрали, как они работают
- Написали простые декораторы
- Познакомились с замыканиями








