Для чего нужны декораторы в Python и чем они отличаются от dependency injection в FastAPI
Что проверяют
Понимаете ли вы, что это инструменты разного уровня: декоратор меняет функцию один раз при её определении, а DI в FastAPI подставляет значения на каждый запрос. Следом обычно спрашивают, как сделать проверку авторизации, и ждут аргументов за Depends.
Как отвечать
- декоратор: функция, которая принимает функцию и возвращает новую. Срабатывает при импорте модуля, запись
@decoэто сахар дляf = deco(f). Декоратор с параметрами это фабрика, которая возвращает декоратор. Внутри обёртки нуженfunctools.wraps, иначе теряются__name__,__doc__и исходная сигнатура. Примеры:@lru_cache,@dataclass,@property, да и сам@app.getв FastAPI тоже декоратор, он регистрирует маршрут; - DI в FastAPI: зависимость это обычная функция или класс, подключённые через
Depends, сейчас принято писатьAnnotated[Session, Depends(get_db)]. FastAPI читает сигнатуру, строит граф зависимостей и вызывает его на каждый запрос. Результат одной зависимости кешируется в пределах запроса. Зависимость сyieldудобна для сессии базы: код доyieldоткрывает, код после закрывает. Параметры зависимостей попадают в OpenAPI, а в тестах любую можно подменить черезapp.dependency_overrides.
Итоговая мысль: сквозная логика вокруг вызова (логирование, ретраи, метрики) это работа декоратора, а то, что эндпоинт получает на вход (текущий пользователь, сессия, настройки), лучше отдать зависимости. Проверку прав на весь роутер вешают через APIRouter(dependencies=[Depends(verify_token)]).
Где спотыкаются
Пишут свой декоратор над эндпоинтом без functools.wraps. FastAPI видит *args, **kwargs вместо параметров, и разбор запроса ломается. Для async def эндпоинта обёртка тоже должна быть асинхронной и делать await.
Ставят свой декоратор выше @app.get. Декораторы применяются снизу вверх, маршрут уже зарегистрировал исходную функцию, и ваша обёртка на запросы не влияет.
Пишут Depends(get_db()) со скобками, хотя туда передаётся сама функция, а не результат вызова. И считают зависимость синглтоном, хотя она вычисляется заново на каждый запрос.