Короткий ответ
functools.wraps — декоратор из стандартного модуля functools, который применяется к функции-обёртке внутри декоратора. Он копирует обёртке метаданные декорируемой функции, чтобы она сохраняла её имя, документацию и другие атрибуты.
Как это работает подробнее
- Проблема. Без
wrapsобёртка затирает метаданные: имя функции становитсяwrapper, пропадают__doc__,__module__и другие атрибуты. - Последствия. В стектрейсах и ошибках фигурирует внутренняя обёртка вместо реальной функции, что ломает интроспекцию, документаторы и инструменты.
- Решение.
@wraps(func)копирует обёртке атрибуты декорируемой функции и обновляет её словарь__dict__, поэтому обёртка «выдаёт себя» за исходную функцию. - Правило. Хорошим тоном считается всегда оборачивать
wrapperв@wraps(func)при написании собственных декораторов.
Пример кода
Декоратор logged с сохранением метаданных
from functools import wraps
def logged(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f'Вызов {func.__name__}')
return func(*args, **kwargs)
return wrapper
@logged
def add(a, b):
"""Складывает два числа."""
return a + b
print(add.__name__) # 'add', а не 'wrapper'
print(add.__doc__) # 'Складывает два числа.'Что использовать на практике
- Каждый декоратор. Применяй
@wraps(func)к обёртке во всех собственных декораторах — это просто и безопасно. - Отладка. Сохранённые
__name__и__doc__делают стектрейсы, логи и справку читаемыми. - Интроспекция. Документаторы и профилировщики корректно работают, когда метаданные функций не потеряны.