← Назад к вопросам

Для каких целей писал декораторы

1.3 Junior🔥 201 комментариев
#Python Core#Soft Skills

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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

Декораторы — один из мощнейших инструментов Python. За 10+ лет я использовал их для решения множества практических задач, от кеширования до контроля доступа.

Логирование и мониторинг

Одно из главных применений — логирование вызовов функций и профилирование производительности.

import functools
import logging
import time

logger = logging.getLogger(__name__)

def log_function_call(func):
    """Логирует все вызовы функции с параметрами и временем выполнения"""
    @functools.wraps(func)
    async def async_wrapper(*args, **kwargs):
        start = time.time()
        logger.info(f"Calling {func.__name__} with args={args}, kwargs={kwargs}")
        try:
            result = await func(*args, **kwargs)
            duration = time.time() - start
            logger.info(f"{func.__name__} completed in {duration:.2f}s")
            return result
        except Exception as e:
            logger.error(f"{func.__name__} failed: {e}", exc_info=True)
            raise
    return async_wrapper

@log_function_call
async def process_order(order_id: int):
    await asyncio.sleep(1)
    return {"status": "success"}

Кеширование результатов

Для оптимизации дорогостоящих операций использую кеширование с TTL.

from datetime import datetime, timedelta
from typing import Any, Callable

def cached(ttl_seconds: int = 300):
    """Кеширует результат функции на указанное время"""
    def decorator(func: Callable) -> Callable:
        cache = {}
        cache_time = {}
        
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            key = (args, tuple(sorted(kwargs.items())))
            now = datetime.now()
            
            if key in cache and now - cache_time[key] < timedelta(seconds=ttl_seconds):
                return cache[key]
            
            result = func(*args, **kwargs)
            cache[key] = result
            cache_time[key] = now
            return result
        
        return wrapper
    return decorator

@cached(ttl_seconds=600)
def get_user_profile(user_id: int):
    # Дорогая операция БД
    return db.query(User).filter(User.id == user_id).first()

Аутентификация и авторизация

Декораторы идеальны для проверки прав доступа в FastAPI и Flask.

from fastapi import Depends, HTTPException, status
from functools import wraps

def require_role(required_role: str):
    """Проверяет, что пользователь имеет нужную роль"""
    async def decorator(func):
        @functools.wraps(func)
        async def wrapper(*args, current_user: User = Depends(get_current_user), **kwargs):
            if current_user.role != required_role:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail="Access denied"
                )
            return await func(*args, **kwargs)
        return wrapper
    return decorator

@app.get("/admin/users")
@require_role("admin")
async def list_users():
    return await User.get_all()

Валидация параметров

Декораторы помогают валидировать входные данные до вызова основной логики.

def validate_email(func):
    """Проверяет, что email параметр валиден"""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        email = kwargs.get('email')
        if email and '@' not in email:
            raise ValueError(f"Invalid email: {email}")
        return func(*args, **kwargs)
    return wrapper

@validate_email
def send_notification(user_id: int, email: str, message: str):
    # Отправляем уведомление
    pass

Retry логика

Для работы с внешними API часто нужны повторные попытки при ошибках.

import asyncio

def retry(max_attempts: int = 3, delay: float = 1.0, backoff: float = 2.0):
    """Повторяет функцию при ошибке с экспоненциальной задержкой"""
    def decorator(func):
        @functools.wraps(func)
        async def wrapper(*args, **kwargs):
            attempt = 0
            current_delay = delay
            
            while attempt < max_attempts:
                try:
                    return await func(*args, **kwargs)
                except Exception as e:
                    attempt += 1
                    if attempt >= max_attempts:
                        raise
                    logger.warning(f"Attempt {attempt} failed, retrying in {current_delay}s: {e}")
                    await asyncio.sleep(current_delay)
                    current_delay *= backoff
        
        return wrapper
    return decorator

@retry(max_attempts=3, delay=1.0)
async def call_external_api(url: str):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            return await resp.json()

Транзакции в БД

Декораторы упрощают управление транзакциями.

from sqlalchemy.ext.asyncio import AsyncSession

def transactional(func):
    """Оборачивает функцию в транзакцию"""
    @functools.wraps(func)
    async def wrapper(session: AsyncSession, *args, **kwargs):
        try:
            result = await func(session, *args, **kwargs)
            await session.commit()
            return result
        except Exception:
            await session.rollback()
            raise
    return wrapper

@transactional
async def transfer_money(session: AsyncSession, from_id: int, to_id: int, amount: decimal.Decimal):
    from_account = await session.get(Account, from_id)
    to_account = await session.get(Account, to_id)
    
    if from_account.balance < amount:
        raise InsufficientFunds()
    
    from_account.balance -= amount
    to_account.balance += amount

Измерение производительности

Для поиска bottleneck'ов использую декораторы профилирования.

import cProfile
import pstats

def profile(func):
    """Профилирует время выполнения функции"""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        profiler = cProfile.Profile()
        profiler.enable()
        
        result = func(*args, **kwargs)
        
        profiler.disable()
        stats = pstats.Stats(profiler)
        stats.sort_stats('cumulative')
        stats.print_stats(10)
        
        return result
    return wrapper

@profile
def heavy_computation():
    # Тяжелые вычисления
    pass

Итоги

Декораторы использую для:

  • Логирования и мониторинга — отслеживание вызовов
  • Кеширования — оптимизация производительности
  • Безопасности — проверка прав доступа
  • Валидации — проверка входных данных
  • Retry логики — обработка временных ошибок
  • Управления транзакциями — автоматический commit/rollback
  • Профилирования — анализ производительности

Это позволяет писать чистый код, избегая повторений и отделяя перекрёстные заботы от основной логики.