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

Какие использовал декораторы?

1.7 Middle🔥 121 комментариев
#FastAPI и Flask#Python Core

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

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

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

Декораторы в Python

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

Что такое декоратор

Декоратор — это функция, которая принимает другую функцию (или класс) как аргумент и возвращает модифицированную версию:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"Вызвана функция {func.__name__}")
        result = func(*args, **kwargs)
        print(f"Функция завершена")
        return result
    return wrapper

@my_decorator
def say_hello(name):
    return f"Hello, {name}!"

print(say_hello("Alice"))
# Вывод:
# Вызвана функция say_hello
# Hello, Alice!
# Функция завершена

1. Встроенные декораторы

@property

Это самый часто используемый декоратор. Он превращает метод в свойство, доступное как атрибут:

class Temperature:
    def __init__(self, celsius):
        self._celsius = celsius
    
    @property
    def celsius(self):
        return self._celsius
    
    @property
    def fahrenheit(self):
        return (self._celsius * 9/5) + 32
    
    @celsius.setter
    def celsius(self, value):
        self._celsius = value

temp = Temperature(20)
print(temp.celsius)      # 20
print(temp.fahrenheit)   # 68.0
temp.celsius = 30        # используется setter

@staticmethod и @classmethod

class Math:
    @staticmethod
    def add(a, b):
        """Статический метод, не требует self"""
        return a + b
    
    @classmethod
    def from_string(cls, value_str):
        """Метод класса, первый параметр — класс"""
        value = int(value_str)
        return cls(value)

print(Math.add(5, 3))  # 8 (не нужно создавать объект)

2. Декораторы для логирования и отладки

Это очень полезно для отслеживания выполнения:

import functools
import time
from typing import Any, Callable

def log_calls(func: Callable) -> Callable:
    @functools.wraps(func)
    def wrapper(*args, **kwargs) -> Any:
        print(f"Вызов: {func.__name__}({args}, {kwargs})")
        result = func(*args, **kwargs)
        print(f"Результат: {result}")
        return result
    return wrapper

@log_calls
def divide(a: float, b: float) -> float:
    return a / b

divide(10, 2)  # Логирует вызов и результат

3. Декоратор для кеширования (memoization)

Оптимизирует повторные вызовы функции:

import functools

def memoize(func):
    cache = {}
    
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        # Создаем ключ кеша
        key = (args, tuple(sorted(kwargs.items())))
        if key not in cache:
            cache[key] = func(*args, **kwargs)
        return cache[key]
    
    return wrapper

@memoize
def fibonacci(n):
    """Вычисляет n-е число Фибоначчи"""
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10))  # Очень быстро благодаря кешу

4. Декоратор для проверки типов

from typing import Any
import functools

def type_check(**expected_types):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            # Проверяем типы параметров
            for param_name, param_value in kwargs.items():
                if param_name in expected_types:
                    expected_type = expected_types[param_name]
                    if not isinstance(param_value, expected_type):
                        raise TypeError(
                            f"{param_name} должна быть {expected_type.__name__}, "
                            f"получена {type(param_value).__name__}"
                        )
            return func(*args, **kwargs)
        return wrapper
    return decorator

@type_check(name=str, age=int)
def create_user(name, age):
    return {"name": name, "age": age}

create_user(name="Alice", age=30)     # OK
# create_user(name="Bob", age="thirty")  # TypeError

5. Декораторы в FastAPI

Это один из самых практичных примеров. FastAPI использует декораторы везде:

from fastapi import FastAPI, Depends, HTTPException
from typing import Optional

app = FastAPI()

# Декоратор для роута
@app.get("/users/{user_id}")
async def get_user(user_id: int):
    return {"user_id": user_id}

# Декоратор с зависимостями (Dependency Injection)
def verify_token(token: Optional[str] = None):
    if not token:
        raise HTTPException(status_code=401, detail="No token")
    return token

@app.post("/protected")
async def protected_endpoint(token: str = Depends(verify_token)):
    return {"message": "You are authenticated", "token": token}

6. Декоратор для обработки ошибок (retry)

Полезно для работы с внешними API или БД:

import functools
import time
from typing import Type

def retry(max_attempts: int = 3, delay: float = 1.0, exceptions: tuple = (Exception,)):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            attempt = 0
            while attempt < max_attempts:
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    attempt += 1
                    if attempt >= max_attempts:
                        raise
                    print(f"Attempt {attempt} failed: {e}. Retrying in {delay}s...")
                    time.sleep(delay)
        return wrapper
    return decorator

@retry(max_attempts=3, delay=1.0, exceptions=(ConnectionError,))
def fetch_data_from_api(url: str):
    # Может выбросить ConnectionError
    pass

7. Декоратор для валидации в Django

from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import csrf_exempt

@require_http_methods(["GET", "POST"])
def my_view(request):
    return HttpResponse("Only GET and POST are allowed")

@csrf_exempt  # Отключить CSRF проверку (не рекомендуется)
def api_endpoint(request):
    pass

8. Собственный декоратор для профилирования

Полезно для отладки производительности:

import functools
import time

def measure_time(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()
        print(f"{func.__name__} took {(end - start)*1000:.2f}ms")
        return result
    return wrapper

@measure_time
def slow_function():
    time.sleep(1)
    return "done"

slow_function()  # slow_function took 1000.23ms

9. Составные декораторы

Можно применять несколько декораторов к одной функции:

@log_calls
@measure_time
@retry(max_attempts=3)
def fetch_data(url):
    # Будет залогирована, будет измерено время, будет retry
    pass

Важные практики

Всегда используй functools.wraps:

import functools

def my_decorator(func):
    @functools.wraps(func)  # Сохраняет __name__, __doc__ и метаданные
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

Без него теряются метаданные функции, и это может сломать документацию и отладку.

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