Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое WRAPS в Python
Примечание: вопрос содержит опечатку — правильное название WRAPS (не WRPAS). @wraps — это декоратор из модуля functools, который сохраняет метаданные исходной функции при создании обёртки. Он решает проблему потери информации о функции при её оборачивании.
Проблема без @wraps
def my_decorator(func):
def wrapper(*args, **kwargs):
"""Обёртка"""
print(f'Вызываю {func.__name__}')
return func(*args, **kwargs)
return wrapper
@my_decorator
def greet(name):
"""Приветствует человека"""
return f'Привет, {name}!'
# Проблема: метаданные теряются
print(greet.__name__) # 'wrapper' (неправильно, должно быть 'greet')
print(greet.__doc__) # 'Обёртка' (неправильно, должно быть документация greet)
print(greet.__module__) # Может быть потеряна информация о модуле
print(greet.__annotations__)# {} (потеряны аннотации типов)
Решение с @wraps
from functools import wraps
def my_decorator(func):
@wraps(func) # Копирует метаданные оригинальной функции
def wrapper(*args, **kwargs):
"""Обёртка"""
print(f'Вызываю {func.__name__}')
return func(*args, **kwargs)
return wrapper
@my_decorator
def greet(name: str) -> str:
"""Приветствует человека"""
return f'Привет, {name}!'
# Теперь всё правильно
print(greet.__name__) # 'greet' ✅
print(greet.__doc__) # 'Приветствует человека' ✅
print(greet.__module__) # '__main__' ✅
print(greet.__annotations__) # {'name': <class 'str'>, 'return': <class 'str'>} ✅
Что копирует @wraps
По умолчанию копируются:
WRAPPER_ASSIGNMENTS = (
'__module__', # Имя модуля
'__name__', # Имя функции
'__qualname__', # Полное квалифицированное имя
'__annotations__', # Аннотации типов
'__doc__', # Docstring
)
WRAPPER_UPDATES = (
'__dict__', # Словарь атрибутов
)
Практические примеры
Декоратор для логирования:
from functools import wraps
import logging
def log_calls(func):
@wraps(func)
def wrapper(*args, **kwargs):
logging.info(f'Вызов {func.__name__} с args={args}, kwargs={kwargs}')
result = func(*args, **kwargs)
logging.info(f'{func.__name__} вернул {result}')
return result
return wrapper
@log_calls
def multiply(a: int, b: int) -> int:
"""Перемножает два числа"""
return a * b
print(multiply.__name__) # 'multiply' ✅
print(multiply.__doc__) # 'Перемножает два числа' ✅
multiply(3, 4) # Логируется с правильным именем
Декоратор для проверки типов:
from functools import wraps
from typing import get_type_hints
def check_types(func):
@wraps(func)
def wrapper(*args, **kwargs):
hints = get_type_hints(func)
# Проверяем типы аргументов
# ...
return func(*args, **kwargs)
return wrapper
@check_types
def add(a: int, b: int) -> int:
"""Складывает два числа"""
return a + b
# get_type_hints(add) работает правильно благодаря @wraps
print(get_type_hints(add)) # {'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}
Декоратор для кэширования:
from functools import wraps, lru_cache
import time
def cache_with_ttl(seconds=60):
def decorator(func):
cache = {}
@wraps(func) # Сохраняет __name__, __doc__, etc.
def wrapper(*args):
if args in cache:
cached_result, timestamp = cache[args]
if time.time() - timestamp < seconds:
return cached_result
result = func(*args)
cache[args] = (result, time.time())
return result
return wrapper
return decorator
@cache_with_ttl(seconds=30)
def expensive_operation(x: int) -> int:
"""Дорогая операция, кэшируется на 30 сек"""
time.sleep(2)
return x ** 2
print(expensive_operation.__name__) # 'expensive_operation' ✅
print(expensive_operation.__doc__) # 'Дорогая операция...' ✅
Декоратор с параметрами и @wraps
from functools import wraps
def repeat(times: int):
"""Декоратор: повторяет функцию N раз"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
results = []
for _ in range(times):
result = func(*args, **kwargs)
results.append(result)
return results
return wrapper
return decorator
@repeat(times=3)
def greet(name: str) -> str:
"""Приветствует человека"""
return f'Привет, {name}!'
print(greet.__name__) # 'greet' ✅
print(greet('Алиса')) # ['Привет, Алиса!', 'Привет, Алиса!', 'Привет, Алиса!']
Важно для документирования
Без @wraps — документация теряется:
def bad_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@bad_decorator
def my_function():
"""Важная функция"""
pass
help(my_function) # Выведет документ wrapper'а, не my_function!
С @wraps — документация сохраняется:
from functools import wraps
def good_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@good_decorator
def my_function():
"""Важная функция"""
pass
help(my_function) # Выведет оригинальную документацию my_function!
Использование с async функциями
from functools import wraps
import asyncio
def async_timer(func):
@wraps(func)
async def wrapper(*args, **kwargs):
start = asyncio.get_event_loop().time()
result = await func(*args, **kwargs)
end = asyncio.get_event_loop().time()
print(f'{func.__name__} заняла {end - start:.2f}сек')
return result
return wrapper
@async_timer
async def fetch_data(url: str) -> str:
"""Получает данные с сервера"""
await asyncio.sleep(1)
return 'data'
print(fetch_data.__name__) # 'fetch_data' ✅
Проверка работы help() и inspect
from functools import wraps
import inspect
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@my_decorator
def process(data: list) -> dict:
"""Обрабатывает данные"""
return {'status': 'ok'}
# Всё работает правильно
print(inspect.signature(process)) # (data: list) -> dict ✅
print(inspect.getsource(process)) # Выведет оригинальную функцию ✅
help(process) # Покажет правильный docstring ✅
Почему @wraps важна
1. Отладка: при ошибке видишь правильное имя функции 2. Документирование: help() и IDE подсказки работают корректно 3. Type checking: mypy видит правильные типы 4. Introspection: inspect модуль работает с оригинальной функцией 5. Testing: мок-объекты корректно работают с декорированными функциями
Best Practice
ВСЕГДА используй @wraps при создании декоратора:
from functools import wraps
def my_decorator(func):
@wraps(func) # ОБЯЗАТЕЛЬНО!
def wrapper(*args, **kwargs):
# твой код здесь
return func(*args, **kwargs)
return wrapper
@wraps — это маленький, но очень важный инструмент, который делает декораторы более прозрачными и совместимыми с инструментами Python для отладки и документирования.