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

Приведи примеры декораторов в Python

2.0 Middle🔥 171 комментариев
#Python Core

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

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

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

Примеры декораторов в Python

Декораторы — это функции высшего порядка, которые принимают функцию/метод и возвращают модифицированную версию. Они позволяют добавлять функциональность без изменения исходного кода. Я использую декораторы постоянно в production коде.

Базовый синтаксис

# Простой декоратор
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 greet(name):
    return f"Привет, {name}"

print(greet("Иван"))
# До вызова greet
# После вызова greet

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

1. Логирование (logging)

import functools
import logging

logger = logging.getLogger(__name__)

def log_calls(func):
    @functools.wraps(func)  # Сохраняет __name__, __doc__ оригинальной функции
    def wrapper(*args, **kwargs):
        logger.info(f"Вызов {func.__name__} с args={args}, kwargs={kwargs}")
        try:
            result = func(*args, **kwargs)
            logger.info(f"Результат: {result}")
            return result
        except Exception as e:
            logger.error(f"Ошибка в {func.__name__}: {e}")
            raise
    return wrapper

@log_calls
def process_payment(user_id, amount):
    return f"Payment {amount} for user {user_id} processed"

2. Кеширование (memoization)

from functools import lru_cache

@lru_cache(maxsize=128)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(35))  # Вычисляется за миллисекунды благодаря кешу

3. Проверка авторизации (auth guard)

from functools import wraps
from fastapi import HTTPException

def require_auth(func):
    @wraps(func)
    def wrapper(request, *args, **kwargs):
        if not request.user or not request.user.is_authenticated:
            raise HTTPException(status_code=401, detail="Not authenticated")
        return func(request, *args, **kwargs)
    return wrapper

@require_auth
def get_user_profile(request):
    return {"user_id": request.user.id}

4. Retry с экспоненциальной задержкой

import time
from functools import wraps

def retry(max_attempts=3, delay=1):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts - 1:
                        raise
                    wait_time = delay * (2 ** attempt)  # Экспоненциальный backoff
                    print(f"Attempt {attempt + 1} failed. Retrying in {wait_time}s...")
                    time.sleep(wait_time)
        return wrapper
    return decorator

@retry(max_attempts=3, delay=1)
def unstable_api_call():
    # Может выбросить исключение
    import random
    if random.random() < 0.7:
        raise Exception("API unavailable")
    return "Success"

5. Проверка типов (validation)

from functools import wraps
from typing import get_type_hints

def validate_types(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        hints = get_type_hints(func)
        # Проверяем типы аргументов
        for i, (arg_name, arg_type) in enumerate(list(hints.items())[:-1]):
            if not isinstance(args[i], arg_type):
                raise TypeError(f"{arg_name} должен быть {arg_type}, получен {type(args[i])}")
        return func(*args, **kwargs)
    return wrapper

@validate_types
def add(a: int, b: int) -> int:
    return a + b

print(add(1, 2))        # OK
print(add("1", "2"))    # TypeError

6. Rate limiting

from functools import wraps
import time
from collections import defaultdict

class RateLimiter:
    def __init__(self, calls, period):
        self.calls = calls
        self.period = period
        self.history = defaultdict(list)
    
    def __call__(self, func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            now = time.time()
            call_history = self.history[func.__name__]
            # Удаляем старые вызовы
            call_history[:] = [t for t in call_history if now - t < self.period]
            
            if len(call_history) >= self.calls:
                raise Exception(f"Rate limit exceeded for {func.__name__}")
            
            call_history.append(now)
            return func(*args, **kwargs)
        return wrapper

limiter = RateLimiter(calls=5, period=60)

@limiter
def api_endpoint():
    return {"status": "ok"}

7. Параметризованный декоратор (timing)

from functools import wraps
import time

def timer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - start
        print(f"{func.__name__} took {elapsed:.4f} seconds")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(0.1)
    return "Done"

slow_function()  # slow_function took 0.1000 seconds

8. Класс как декоратор

class Counter:
    def __init__(self, func):
        self.func = func
        self.count = 0
    
    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"Функция вызвана {self.count} раз")
        return self.func(*args, **kwargs)

@Counter
def hello():
    return "Hello"

hello()  # Функция вызвана 1 раз
hello()  # Функция вызвана 2 раз

Встроенные декораторы Python

class MyClass:
    @property  # Превращает метод в свойство
    def name(self):
        return self._name
    
    @staticmethod  # Не требует self
    def static_method():
        return "static"
    
    @classmethod  # Получает класс вместо self
    def from_dict(cls, data):
        return cls(**data)

Выводы: декораторы — это мощный паттерн для разделения concerns (логирование, кеширование, авторизация). Правильное использование делает код чище и переиспользуемым.

Приведи примеры декораторов в Python | PrepBro