Зачем обёртке декоратора нужен атрибут __wrapped__?
Атрибут __wrapped__ связывает обёртку с исходной функцией. Благодаря этому инструменты интроспекции, например inspect.signature() и inspect.unwrap(), могут обнаружить исходный вызываемый объект вместо того, чтобы видеть только техническую обёртку.
Обычно атрибут устанавливает functools.wraps. Он улучшает совместимость декораторов с отладчиками, документацией, тестовыми инструментами и фреймворками, но не превращает обёртку в исходную функцию и не отменяет её поведение.
Декоратор часто заменяет исходную функцию новой функцией-обёрткой. Обёртка добавляет логирование, проверку доступа, измерение времени или обработку ошибок, но при этом может скрыть имя, документацию и сигнатуру исходной функции.
Для прикладного кода важно не только вызвать функцию, но и исследовать её: построить документацию, определить параметры, найти исходный обработчик или корректно показать ошибку. Поэтому стандартный механизм декораторов предусматривает перенос метаданных и ссылку на завернутый объект.
Без __wrapped__ интроспекция обычно видит параметры самой обёртки, например *args и **kwargs, а не реальные параметры исходной функции. Это может нарушить генерацию документации, автоматическую передачу зависимостей, проверку сигнатур и работу инструментов отладки.
При этом копирование только __name__ и __doc__ проблему не решает: эти атрибуты описывают функцию текстово, но не дают стандартного пути к исходному вызываемому объекту. Неверное понимание этого механизма приводит к ошибочному ожиданию, что метаданные автоматически сохраняются у любого декоратора.
functools.wraps — это удобная форма применения functools.update_wrapper к внутренней функции. Помимо переноса ряда атрибутов, update_wrapper устанавливает у обёртки __wrapped__, ссылающийся на исходную функцию.
При вызове inspect.signature(add) функция инспекции по умолчанию проходит по цепочке __wrapped__ и получает сигнатуру исходной функции. inspect.unwrap позволяет пройти эту цепочку явно; если декораторов несколько, каждый корректно созданный слой может указывать на предыдущий.
__wrapped__ не меняет порядок выполнения и не удаляет логику декоратора. Вызов всё равно сначала попадает в обёртку. Также атрибут не гарантирует полное сохранение всех возможных пользовательских атрибутов: wraps копирует стандартный набор метаданных и обновляет словарь атрибутов по правилам update_wrapper.
Использовать wraps следует почти всегда при написании функционального декоратора. Исключения возможны, когда намеренно требуется скрыть исходную функцию от интроспекции или создать собственную модель метаданных, но это должно быть осознанным решением.
В веб-приложении декоратор авторизации оборачивает обработчики маршрутов. Без wraps все обработчики выглядят для инструмента регистрации как функция с произвольными *args и **kwargs; генерация документации и автоматическая проверка параметров становятся неточными.
Рассматривались два варианта. Можно вручную присваивать __name__, __doc__ и другие атрибуты, но это хрупко: легко забыть __wrapped__, а набор метаданных сложнее поддерживать. Можно было отказаться от обёртки и реализовать авторизацию в отдельном слое диспетчера, но это потребовало бы изменить архитектуру и усложнило локальное применение правила.
Выбрали декоратор с functools.wraps. Бизнес-логика авторизации сохранилась в обёртке, а инструменты получили доступ к исходной сигнатуре через __wrapped__. При этом команда отдельно проверила, что безопасность не зависит от данных интроспекции: __wrapped__ предназначен для анализа, а не для обхода самой проверки.
__name__ и __doc__, чтобы inspect.signature увидел исходную сигнатуру?Нет. Эти атрибуты влияют на отображаемое имя и документацию, но не описывают связь обёртки с исходной функцией. Для стандартного перехода к исходному объекту нужен __wrapped__ или явно заданный механизм получения сигнатуры, например __signature__.
__wrapped__, что декоратор можно обойти обычным вызовом?Нет. __wrapped__ — это доступная ссылка на объект для интроспекции, а не автоматический переключатель поведения. Обычный вызов обёрнутой функции по-прежнему выполняет код декоратора. Однако код, который сознательно обращается к __wrapped__, может вызвать исходную функцию напрямую, поэтому публикация этой ссылки должна учитываться при проектировании API и модели безопасности.
functools.wraps?Цепочка интроспекции оборвётся на таком слое: инструменты смогут увидеть эту обёртку, но не смогут стандартно пройти к функциям под ней. Верхние декораторы, использующие wraps, сохранят ссылку только на ближайший доступный слой. Поэтому при создании композиции декораторов важно применять wraps на каждом функциональном уровне и помнить, что любой декоратор может намеренно скрыть дальнейшую структуру.