ПрограммированиеBackend разработчик

Что такое декораторы функций высшего порядка в Python, как они позволяют реализовать паттерн 'обёртывания', и что следует учитывать при работе с метаданными/документацией функции?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

В Python декоратор функции высшего порядка — это функция, принимающая другую функцию (или класс) и возвращающая новую функцию (или новый модифицированный класс). Декораторы часто используют для реализации паттерна "обёртывания" (wrapping), позволяющего добавлять дополнительную логику (например, логирование, кеширование, проверку прав и т.д.) к уже существующим функциям без изменения их исходного кода.

Для сохранения имени, документации и других метаданных оригинальной функции рекомендуется использовать функцию functools.wraps:

import functools def log_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print(f'Calling {func.__name__}') return func(*args, **kwargs) return wrapper @log_decorator def add(a, b): """Add two numbers""" return a + b print(add(2, 3)) # Output: Calling add 5 print(add.__name__) # Output: add print(add.__doc__) # Output: Add two numbers

Ключевой момент: без functools.wraps у обёрнутой функции потеряются имя, документация и прочие метаданные оригинала, что негативно влияет на отладку и автодокументирование.

Вопрос с подвохом.

Если задекорировать функцию без использования functools.wraps, что произойдёт с атрибутами name и doc функции?

Ответ: Они будут унаследованы от внутренней функции-обёртки (обычно 'wrapper'), и вы потеряете оригинальные метаданные.

def simple_decorator(func): def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper @simple_decorator def f(): """This is docstring""" pass print(f.__name__) # Output: 'wrapper' (НЕ 'f') print(f.__doc__) # Output: None (а не 'This is docstring')

Примеры реальных ошибок из-за незнания тонкостей темы.


История

В проекте реализовали сложную систему декораторов для журналирования API-endpoint'ов, но не применяли functools.wraps. В результате, автогенерация документации (Swagger/OpenAPI) и инструменты интроспекции показывали имена всех endpoint'ов как 'wrapper', а документация исчезла. Это сильно затрудняло поддержку, тестирование и сопровождение.


История

При написании unit tests с помощью pytest произошёл сбой auto-discovery тестов: тестовые функции, задекорированные своими декораторами без wraps, не обнаруживались, потому что их name был некорректным. Причина — pytest ищет функции по имени.


История

При трассировке стека исключений (traceback) stack-trace указывал всегда на "wrapper", и было невозможно понять, какая именно функция вызвала ошибку, поскольку root-метаданные потерялись из-за отсутствия functools.wraps в пользовательских декораторах.