Короткий ответ
Без functools.wraps декорированная функция теряет собственные __name__ и __doc__, поэтому help() и обращение к метаданным покажут данные служебной функции wrapper, а не оригинальной функции greet.
Как это работает подробнее
Декоратор log_time оборачивает greet в wrapper и возвращает wrapper без копирования атрибутов; имя функции greet после декорирования фактически заменяется на wrapper, потому что greet = log_time(greet) присваивает переменной greet объект wrapper.
Пример кода
example-260.py
# Намеренно без functools.wraps - для анализа
def log_time(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@log_time
def greet(name):
"""Приветствует пользователя по имени."""
return f"Hello, {name}"
print(greet.__name__) # "wrapper", а не "greet"
print(greet.__doc__) # None, а не "Приветствует пользователя по имени."Что использовать на практике
- Практика. Это заметно при отладке (в traceback будет «wrapper», а не «greet»), при автогенерации документации по
__doc__/__name__, а также при использовании интроспекции.
Подводные камни
- Риски. Не замечают потерю метаданных, пока не начинают использовать инструменты, которые на них опираются (автогенерация OpenAPI-схем по именам и докстрингам хендлеров, профилировщики, группирующие вызовы по
__name__).