Какие задачи решал с помощью своих декораторов в Python
Что проверяют
Понимаете ли вы, что происходит под @, и есть ли у вас реальные случаи, где декоратор убрал повторяющийся код. Проверяют и практику, и механику: после примеров обычно просят написать декоратор с аргументами или объяснить, что делает functools.wraps.
Как отвечать
Механика в двух фразах: @deco над функцией означает f = deco(f) в момент определения, то есть при импорте модуля. Декоратор с параметрами (@retry(times=3)) это фабрика, которая возвращает декоратор, три уровня вложенности. Бывают и классы-декораторы с __call__, когда нужно хранить состояние.
Потом конкретные задачи из бэкенда:
- доступ и авторизация: проверка роли перед обработчиком, по мотивам
login_requiredиз Django. В FastAPI для этого правильнее зависимости черезDepends, и это стоит сказать; - ретраи: повтор вызова внешнего API на
ConnectionErrorс экспоненциальной паузой и ограничением числа попыток; - метрики и логирование: время выполнения, счётчик ошибок, контекст запроса в логах;
- транзакции: открыть транзакцию, откатить на исключении. Так устроен
transaction.atomicв Django, он работает и как декоратор, и как контекстный менеджер; - регистрация:
@app.route,@router.get, обработчики событий, плагины. Декоратор кладёт функцию в реестр и возвращает её без изменений.
Покажите, что знаете детали. functools.wraps копирует метаданные и ставит __wrapped__, по нему inspect.signature находит исходную сигнатуру, на этом держатся внедрение зависимостей в FastAPI и фикстуры pytest. Для типизации обёртки есть ParamSpec (Python 3.10 и новее), тогда mypy видит настоящие аргументы.
Где спотыкаются
Оборачивают async def синхронной обёрткой. Она вернёт корутину, не дождавшись её, и замер времени покажет ноль. Для корутин обёртка тоже должна быть async и делать await.
Забывают wraps. В логах все функции называются wrapper, а фреймворк, который читает сигнатуру, перестаёт видеть параметры.
Вешают @lru_cache на метод. Кэш держит ссылку на self, и объекты не освобождаются, пока жив кэш. Для вычисляемых атрибутов есть functools.cached_property, для методов с аргументами лучше кэш на уровне экземпляра.