Как реализовать инверсию управления в Python?
Что проверяют
Различаете ли вы инверсию управления, внедрение зависимостей и принцип инверсии зависимостей, и не тащите ли в Python тяжёлые контейнеры по привычке из Java. Следом спрашивают, как это помогает в тестах и нужен ли DI-фреймворк.
Как отвечать
Инверсия управления значит, что не ваш код вызывает инфраструктуру, а фреймворк вызывает ваш код. Примеры, которые все видели: Django сам вызывает view, pytest сам подставляет фикстуры по имени аргумента, FastAPI сам разрешает Depends. Внедрение зависимостей это частный случай: объект не создаёт то, что ему нужно, а получает снаружи.
Каркас ответа:
- внедрение через конструктор:
OrderService(repo: OrderRepository, notifier: Notifier). Сервис не знает, откуда пришли зависимости; - абстракция: в Python удобнее
typing.Protocol, чемABC. Протокол структурный, реализация не обязана от него наследоваться, а mypy или pyright проверят совместимость; - корень композиции: объекты собираются в одном месте, в
main()или фабрике приложения. Дальше никто не создаёт зависимости сам; - фреймворки: в FastAPI это
Dependsиapp.dependency_overridesдля подмены в тестах, есть библиотеки вродеdependency-injector,injector,punq. Небольшому сервису хватает ручной сборки; - тесты: в сервис передаётся фейковый репозиторий в памяти, без
mock.patch.
Сильный завершающий тезис: в динамическом языке DI это прежде всего дисциплина того, где создаются объекты, а не обязательный контейнер.
Где спотыкаются
Пишут def __init__(self, repo=PostgresRepo()). Значение по умолчанию вычисляется один раз при определении функции, и все экземпляры делят одно подключение. Правильно сделать аргумент обязательным или писать repo: OrderRepository | None = None и создавать объект внутри.
Заводят глобальный контейнер и достают из него зависимости внутри методов. Это сервис-локатор: зависимости снова скрыты, и без чтения кода не понять, что нужно классу.
Вместо внедрения патчат всё через mock.patch и ошибаются путём. Патчить нужно имя там, где его ищут, а не где определили, иначе тест обращается к настоящей зависимости.