Ответ
Декоратор @functools.wraps используется для того, чтобы функция-обертка, создаваемая декоратором, наследовала метаданные (имя, docstring, аннотации типов, аргументы) от исходной декорируемой функции.
Проблема, которую он решает:
Без @wraps метаданные оригинальной функции заменяются метаданными функции-обертки. Это затрудняет отладку (например, в стектрейсах отображается имя wrapper), а также нарушает работу инструментов для интроспекции и автоматической генерации документации.
Пример без @wraps:
def my_decorator(func):
def wrapper(*args, **kwargs):
"""Это документация обертки."""
print("Что-то до вызова функции.")
return func(*args, **kwargs)
return wrapper
@my_decorator
def say_hello():
"""Это документация оригинальной функции."""
print("Привет!")
print(say_hello.__name__) # Выведет: 'wrapper'
print(say_hello.__doc__) # Выведет: 'Это документация обертки.'
Пример с @wraps:
from functools import wraps
def my_decorator(func):
@wraps(func) # Копируем метаданные из func в wrapper
def wrapper(*args, **kwargs):
"""Это документация обертки (игнорируется благодаря @wraps)."""
print("Что-то до вызова функции.")
return func(*args, **kwargs)
return wrapper
@my_decorator
def say_hello():
"""Это документация оригинальной функции."""
print("Привет!")
print(say_hello.__name__) # Выведет: 'say_hello'
print(say_hello.__doc__) # Выведет: 'Это документация оригинальной функции.' Ответ 18+ 🔞
Давай я тебе объясню про этот самый @functools.wraps, а то без него декораторы превращаются в такую жесть, что потом сам себя не узнаешь в отладчике, блядь.
Представь, ты написал декоратор — обёртку для функции. Красиво, модно, молодёжно. Но без @wraps твоя обёртка начинает вести себя как полная пиздопроебина: она крадёт у оригинальной функции её имя, её документ-стринг, её аннотации — всё, что делает её узнаваемой. В итоге в стектрейсе вместо родного имени функции ты видишь какого-то унылого wrapper, а документация твоя летит в пизду.
Вот смотри, как это выглядит без этого самого @wraps:
def my_decorator(func):
def wrapper(*args, **kwargs):
"""Это документация обертки."""
print("Что-то до вызова функции.")
return func(*args, **kwargs)
return wrapper
@my_decorator
def say_hello():
"""Это документация оригинальной функции."""
print("Привет!")
print(say_hello.__name__) # Выведет: 'wrapper'
print(say_hello.__doc__) # Выведет: 'Это документация обертки.'
Видишь? Функция say_hello теперь зовётся wrapper, а её родная документация подменена на какую-то хуйню. Это как если бы Герасим из той истории вместо «Муму» начал орать «Я — Герасим, блядь!». Полный пиздец для интроспекции.
А теперь смотрим, как надо делать, с @wraps:
from functools import wraps
def my_decorator(func):
@wraps(func) # Вот эта магия копирует метаданные из func в wrapper
def wrapper(*args, **kwargs):
"""Это документация обертки (игнорируется благодаря @wraps)."""
print("Что-то до вызова функции.")
return func(*args, **kwargs)
return wrapper
@my_decorator
def say_hello():
"""Это документация оригинальной функции."""
print("Привет!")
print(say_hello.__name__) # Выведет: 'say_hello'
print(say_hello.__doc__) # Выведет: 'Это документация оригинальной функции.'
Вот теперь — красота! Функция сохранила своё имя и свою душу — документацию. @wraps просто аккуратно копирует все метаданные из оригинальной функции в обёртку, и все инструменты для отладки или генерации документации работают как надо. Без этой штуки ты будешь как тот немой Герасим — будешь мычать «Муму», а тебя никто не поймёт, блядь.