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

Как декораторы влияют на читаемость кода?

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

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

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

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

Декораторы и читаемость кода

Декораторы — это один из мощнейших инструментов Python, но их неправильное использование может превратить код в магию, которую никто не понимает. Вопрос о читаемости очень важен.

Когда декораторы улучшают читаемость

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

# Без декоратора — нужно помнить вызывать функцию с логированием
def register_user(username: str) -> User:
    logger.info(f"Registering user: {username}")
    user = User.create(username)
    logger.info(f"User registered: {user.id}")
    return user

# С декоратором — логирование наклеивается автоматически
from functools import wraps

def log_calls(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        logger.info(f"Calling {func.__name__} with args={args}, kwargs={kwargs}")
        result = func(*args, **kwargs)
        logger.info(f"{func.__name__} returned {result}")
        return result
    return wrapper

@log_calls
def register_user(username: str) -> User:
    return User.create(username)

В первом варианте мы повторяем логирование везде. Во втором — всё что нужно узнать о логировании видно в одной строке.

Когда декораторы усложняют читаемость

Но есть и обратная сторона:

# Слишком много вложенных декораторов
@cache
@validate_auth
@check_permission(admin)
@rate_limit(100)
@log_calls
@measure_performance
def sensitive_operation():
    pass

Читатель кода должен понять порядок выполнения, что требует глубокого понимания того, как декораторы оборачивают друг друга.

Правила использования для сохранения читаемости

1. Один декоратор — одна ответственность:

@cached_property
def expensive_value(self):
    return sum(self.items)

2. Используй functools.wraps:

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

Без @wraps(func) отладка становится кошмаром.

3. Декораторы с параметрами требуют документации:

def retry(max_attempts: int, backoff: float = 1.0):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            attempt = 0
            wait = 1.0
            while attempt < max_attempts:
                try:
                    return func(*args, **kwargs)
                except Exception:
                    attempt += 1
                    if attempt >= max_attempts:
                        raise
                    time.sleep(wait)
                    wait *= backoff
        return wrapper
    return decorator

4. Максимум 2-3 декоратора на функцию:

@cache
@validate_auth
def get_user_profile():
    pass

Практический пример: кейс из реальной работы

def require_permission(permission: str):
    def decorator(func):
        @wraps(func)
        def wrapper(request, *args, **kwargs):
            if not request.user:
                raise Unauthenticated()
            if not request.user.has_permission(permission):
                raise PermissionDenied(f"Requires: {permission}")
            return func(request, *args, **kwargs)
        return wrapper
    return decorator

@require_permission(post.edit)
def edit_post(request, post_id: int):
    pass

Итоговые рекомендации

  • Используй встроенные декораторы: @property, @staticmethod, @classmethod, @cached_property — они всем понятны
  • Придумай понятное имя: @log_calls лучше, чем @log
  • Документируй нестандартные декораторы
  • Ограничивай вложенность: 2-3 — нормально, 5+ — переделай архитектуру
  • Тестируй отдельно

Декораторы — это не о магии. Это о абстракции, которая скрывает деталь реализации, но должна быть ясна из названия и документации.

Как декораторы влияют на читаемость кода? | PrepBro