Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Декораторы в Python: мощный инструмент метапрограммирования
Декораторы — это один из самых элегантных и мощных механизмов Python. За мои 10+ лет разработки я использовал их в десятках проектов для улучшения качества кода и добавления функциональности.
Что такое декоратор
Декоратор — это функция, которая принимает другую функцию в качестве аргумента и возвращает новую функцию с дополнительной функциональностью. Это позволяет обернуть функцию без изменения её исходного кода.
# Простейший пример декоратора
def my_decorator(func):
def wrapper(*args, **kwargs):
print(f"Вызываю {func.__name__}")
result = func(*args, **kwargs)
print(f"Завершён {func.__name__}")
return result
return wrapper
@my_decorator
def hello(name):
print(f"Привет, {name}!")
hello("Мир")
# Output:
# Вызываю hello
# Привет, Мир!
# Завершён hello
Практические примеры декораторов
Логирование и мониторинг
import logging
import time
from functools import wraps
def timing_logger(func):
@wraps(func) # Сохраняет метаинформацию оригинальной функции
def wrapper(*args, **kwargs):
start = time.time()
try:
result = func(*args, **kwargs)
logging.info(f"{func.__name__} выполнена за {time.time() - start:.2f}s")
return result
except Exception as e:
logging.error(f"{func.__name__} ошибка: {e}", exc_info=True)
raise
return wrapper
@timing_logger
def process_data(items):
time.sleep(1) # Долгая операция
return len(items)
Валидация аргументов
from typing import Callable
def validate_positive(*param_names):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Проверяем, что аргументы положительные
for param in param_names:
if param in kwargs and kwargs[param] < 0:
raise ValueError(f"{param} должен быть положительным")
return func(*args, **kwargs)
return wrapper
return decorator
@validate_positive('amount', 'rate')
def calculate_interest(amount, rate):
return amount * rate / 100
calculate_interest(amount=100, rate=-5) # Raises ValueError
Кеширование результатов (Memoization)
from functools import lru_cache
@lru_cache(maxsize=128)
def expensive_computation(n):
# Результаты будут кешированы
return sum(i**2 for i in range(n))
# Первый вызов: вычисляет
expensive_computation(10000)
# Второй вызов: берёт из кеша
expensive_computation(10000) # Мгновенно!
Управление доступом (Permissions)
from functools import wraps
from flask import request, jsonify
def require_role(role):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
user_role = request.headers.get('X-User-Role')
if user_role != role:
return jsonify({"error": "Access denied"}), 403
return func(*args, **kwargs)
return wrapper
return decorator
@require_role('admin')
def delete_user(user_id):
# Только admins могут удалять пользователей
return {"status": "deleted"}
Асинхронные операции (Async/Await)
import asyncio
from functools import wraps
def async_timeout(seconds=5):
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
try:
return await asyncio.wait_for(
func(*args, **kwargs),
timeout=seconds
)
except asyncio.TimeoutError:
raise Exception(f"Function timed out after {seconds}s")
return wrapper
return decorator
@async_timeout(seconds=3)
async def fetch_data(url):
# Будет отменена, если выполняется дольше 3 секунд
result = await some_http_request(url)
return result
Декораторы классов
Singleton паттерн
def singleton(cls):
instances = {}
@wraps(cls)
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class DatabaseConnection:
def __init__(self, connection_string):
self.connection_string = connection_string
# Всегда возвращает один и тот же объект
db1 = DatabaseConnection("postgres://...")
db2 = DatabaseConnection("postgres://...")
assert db1 is db2 # True!
Dataclass с валидацией
from dataclasses import dataclass, field
def validated_dataclass(cls):
"""Добавляет валидацию к dataclass"""
@wraps(cls.__init__)
def new_init(self, **kwargs):
# Валидируем перед инициализацией
for key, value in kwargs.items():
if not isinstance(value, cls.__annotations__[key]):
raise TypeError(f"{key} должен быть {cls.__annotations__[key]}")
cls.__init__(self, **kwargs)
cls.__init__ = new_init
return cls
Встроенные полезные декораторы Python
class MyClass:
# property — превращает метод в атрибут
@property
def full_name(self):
return f"{self.first_name} {self.last_name}"
# staticmethod — метод класса без доступа к self
@staticmethod
def from_string(data):
first, last = data.split()
return MyClass(first, last)
# classmethod — метод класса с доступом к cls
@classmethod
def from_dict(cls, data):
return cls(data['first_name'], data['last_name'])
Когда использовать декораторы
- Логирование и мониторинг функций
- Валидация входных данных
- Кеширование результатов
- Контроль доступа (permissions, authentication)
- Трансформация результатов функции
- Повторные попытки при ошибках (retry logic)
- Реалтайм мониторинг performance
Декораторы — это элегантный способ добавить функциональность без изменения исходного кода. Они делают код чище, переиспользуемым и соответствующим принципу DRY.