Логотип YeaHub

База вопросов

Собеседования

Тренажёр

База ресурсов

Обучение

Навыки

Задачи

Войти

Выбери, каким будет IT завтра — вместе c нами!

YeaHub — это полностью открытый проект, призванный объединить и улучшить IT-сферу. Наш исходный код доступен для просмотра на GitHub. Дизайн проекта также открыт для ознакомления в Figma.

© 2026 YeaHub

AI info

Карта сайта

Документы

Медиа

Назад
Вопрос про Python: decorator, wrapper, functools

Что такое паттерн Decorator и как он реализован в Python?

Вопрос проверяет понимание структурных паттернов и того, как в Python устроены декораторы функций/методов и расширение поведения через обёртки.

Короткий ответ

Decorator — это паттерн, который добавляет поведение объекту через «обёртку», не меняя исходный класс/функцию. В Python под «декоратором» чаще всего понимают функцию, которая принимает другую функцию и возвращает новую функцию-обёртку. Это удобно для логирования, кэширования, проверки прав, ретраев. Важно корректно прокидывать аргументы и сохранять метаданные функции.

Длинный ответ

Определение

Decorator (паттерн) — это структурный паттерн, который позволяет динамически добавлять поведение объекту, оборачивая его другим объектом с тем же интерфейсом.

В Python слово «декоратор» часто используют в более узком смысле: синтаксис @decorator для функций и методов, который подменяет исходную функцию на обёрнутую.

Зачем нужен Decorator

Обычно его применяют, когда нужно:

  1. Добавить сквозную функциональность (cross-cutting concerns)

    • логирование

    • метрики

    • трейсинг

    • контроль доступа

  2. Не раздувать бизнес-код повторяющимися проверками

  3. Сохранять возможность комбинировать несколько «надстроек»

Как работает декоратор функции в Python

Декоратор — это вызываемый объект, который:

  1. Принимает функцию func

  2. Создаёт wrapper(*args, **kwargs)

  3. Возвращает wrapper

Простой пример:

import time

def timing(func):
    def wrapper(*args, **kwargs):
        started = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - started
        print(f"{func.__name__} took {elapsed:.3f}s")
        return result
    return wrapper

@timing
def slow(x):
    time.sleep(0.1)
    return x * 2

Почему нужен functools.wraps

Без него теряются метаданные функции: __name__, __doc__, сигнатура становится менее понятной.

from functools import wraps

def timing(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # ... та же логика
        return func(*args, **kwargs)
    return wrapper

Это важно для:

  • логирования по имени функции

  • документации

  • интроспекции

  • некоторых фреймворков (например, роутинг/DI)

Декоратор с параметрами

Иногда нужен декоратор с конфигурацией. Тогда появляется дополнительный уровень:

  1. decorator_factory(config) возвращает decorator

  2. decorator(func) возвращает wrapper

from functools import wraps
import time

def retry(times, delay_sec=0.0):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            last_exc = None
            for _ in range(times):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    last_exc = e
                    if delay_sec:
                        time.sleep(delay_sec)
            raise last_exc
        return wrapper
    return decorator

@retry(times=3, delay_sec=0.1)
def fragile():
    ...

Decorator как паттерн для объектов

Паттерн «Decorator» в ООП — это когда у вас есть интерфейс и обёртка, которая реализует тот же интерфейс, но добавляет логику до/после.

Схема:

  1. Базовый интерфейс

  2. Реальная реализация

  3. Декоратор хранит ссылку на объект и проксирует вызовы, добавляя поведение

Упрощённый пример:

class Storage:
    def get(self, key):
        raise NotImplementedError

class RedisStorage(Storage):
    def get(self, key):
        return "value"  # условно

class LoggingStorage(Storage):
    def __init__(self, storage):
        self._storage = storage

    def get(self, key):
        print("get:", key)
        return self._storage.get(key)

Частые ошибки на практике

  1. Не прокидывают *args, **kwargs

  2. Не возвращают результат исходной функции

  3. Ломают исключения (например, глотают их без необходимости)

  4. Не используют @wraps, из-за чего страдают метрики/интроспекция

  5. Делают слишком «тяжёлые» декораторы, которые трудно тестировать

Краткий вывод

Decorator применяют, когда нужно добавлять поведение поверх существующей логики без изменения исходного кода. В Python это естественно выражается через @decorator, а для качества кода важно корректно обрабатывать аргументы, возвращаемые значения и метаданные (functools.wraps).

  • Аватар

    Python Guru

    Sergey Filichkin

    Guru – это эксперты YeaHub, которые помогают развивать комьюнити.

Уровень

  • Рейтинг:

    5

  • Сложность:

    6

Навыки

  • Python

    Python

Ключевые слова

#decorator

#wrapper

#functools

Подпишись на Python Developer в телеграм

  • Аватар

    Python Guru

    Sergey Filichkin

    Guru – это эксперты YeaHub, которые помогают развивать комьюнити.