Что такое декоратор? Как написать собственный декоратор?

В Python декоратор — это функция, которая принимает другую функцию и возвращает новую функцию с изменённым или дополненным поведением, не изменяя исходный код.
Это мощный инструмент, который позволяет писать чистый, переиспользуемый и лаконичный код.

🔷 Что такое декоратор простыми словами?

Декоратор — это как обёртка для функции. Он «улучшает» или добавляет функциональность, не трогая саму функцию.

📦 Представь: у тебя есть подарок (функция), и ты завернул его в красивую обёртку (декоратор) — внутри всё тот же подарок, но снаружи он выглядит лучше или делает что-то ещё.

✅ Синтаксис декоратора:

@decorator_name
def my_function():
...
Это сокращение от:
def my_function():
...
my_function = decorator_name(my_function)

🔧 Как работает декоратор?

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

🔍 Пример:

def my_decorator(func):
def wrapper():
print("Перед вызовом функции")
func()
print("После вызова функции")
return wrapper
@my_decorator
def say_hello():
print("Привет!")
say_hello()

📤 Вывод:

Перед вызовом функции

Привет!

После вызова функции

🔨 Как написать собственный декоратор?

Шаг 1: Напиши функцию-декоратор, принимающую другую функцию.Шаг 2: Определи вложенную wrapper-функцию.Шаг 3: Верни эту wrapper.

💡 Пример с аргументами:

def decorator(func):
def wrapper(\*args, \*\*kwargs): # передаёт любые аргументы
print("Вызов функции с аргументами:", args, kwargs)
result = func(\*args, \*\*kwargs)
print("Результат:", result)
return result
return wrapper
@decorator
def add(a, b):
return a + b
add(2, 3)

📤 Вывод:

Вызов функции с аргументами: (2, 3) {}

Результат: 5

📌 Зачем нужны декораторы?

  • Логирование (запись в журнал)

  • **Авторизация/проверка доступа
    **

  • **Измерение времени работы функции
    **
  • **Кэширование
    **
  • **Повторное выполнение при ошибке
    **
  • **Валидация данных
    **

🧪 Примеры полезных декораторов

1. Логирование:

def log(func):
def wrapper(\*args, \*\*kwargs):
print(f"Вызов {func.\__name_\_} с {args}, {kwargs}")
return func(\*args, \*\*kwargs)
return wrapper
@log
def greet(name):
return f"Привет, {name}!"
greet("Миша")

2. Измерение времени:

import time
def timer(func):
def wrapper(\*args, \*\*kwargs):
start = time.time()
res = func(\*args, \*\*kwargs)
end = time.time()
print(f"Функция {func.\__name_\_} работала {end - start:.4f} секунд")
return res
return wrapper
@timer
def long_task():
time.sleep(1)
long_task()

3. Повторная авторизация (пример с проверкой):

def authorized(func):
def wrapper(\*args, \*\*kwargs):
if kwargs.get("user") != "admin":
print("Доступ запрещён")
return None
return func(\*args, \*\*kwargs)
return wrapper
@authorized
def delete_data(user=None):
print("Данные удалены")
delete_data(user="guest") # Доступ запрещён
delete_data(user="admin") # Данные удалены

🎯 Особенности:

  1. **Декораторы можно накладывать друг на друга:
    **
@dec1
@dec2
def func():
...
\# сначала dec2, потом dec1
  1. **Функция functools.wraps сохраняет имя и документацию:
    **
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(\*args, \*\*kwargs):
return func(\*args, \*\*kwargs)
return wrapper
  1. Можно писать декораторы с параметрами — это вложенные функции:
def repeat(times):
def decorator(func):
def wrapper(\*args, \*\*kwargs):
for _ in range(times):
func(\*args, \*\*kwargs)
return wrapper
return decorator
@repeat(3)
def hello():
print("Привет")
hello()