Что такое контейнеры зависимостей
Что проверяют
Понимаете ли вы, зачем контейнер нужен в Python, где любая зависимость подменяется в тесте одной строкой патча. За вопросом стоит умение различать три разные вещи: принцип инверсии зависимостей, приём внедрения через параметры и сам контейнер как инструмент сборки графа объектов.
Как отвечать
Контейнер это реестр рецептов: он знает, из чего собирается объект, откуда взять его зависимости и сколько экземпляров держать. Код объявляет, что ему нужен интерфейс, контейнер отдаёт готовую реализацию и владеет её временем жизни.
По существу механизмов три. Регистрация: провайдер описывает, как создать объект, и читает настройки из окружения. Разрешение: граф зависимостей собирается лениво, при первом обращении, по типам или по именам. Области видимости: один экземпляр на процесс, отдельный на HTTP-запрос, отдельный на фоновую задачу.
Из инструментов уместно назвать dependency-injector с декларативными контейнерами и переопределением провайдеров в тестах, injector в стиле Guice, а также Depends в FastAPI: это тот же контейнер уровня запроса, просто встроенный во фреймворк.
Главный практический довод: точка сборки приложения одна, и тест подменяет реализацию честным переопределением провайдера, а не патчем по строковому пути модуля, который молча ломается при первом переименовании.
Где спотыкаются
Превращают контейнер в сервис-локатор: функция сама лезет в глобальный контейнер и достаёт из него зависимость. Сигнатура снова молчит о том, что объекту нужно, и подменить это в тесте так же трудно, как было до контейнера.
Тащат контейнер в маленький проект. Пока зависимостей три и все собираются в точке входа, обычный параметр конструктора честнее и читается без документации.
Забывают про область видимости и регистрируют синглтоном то, что хранит состояние: сессию базы, клиента с привязкой к событийному циклу, открытое соединение. В многопоточном и асинхронном коде это даёт плавающие ошибки, которые воспроизводятся только под нагрузкой.