Какие существуют способы внедрения зависимостей в Python без использования DI-фреймворков

«Какие существуют способы внедрения зависимостей в Python без использования DI-фреймворков» — вопрос из категории Паттерны, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Python внедрение зависимостей (Dependency Injection) можно реализовать несколькими способами без привлечения специализированных фреймворков, следуя принципу "явное лучше неявного".

  1. Через конструктор (Constructor Injection) Это самый распространенный и предпочтительный способ. Зависимости передаются как аргументы в метод __init__.

    class DatabaseClient:
        def query(self, sql: str) -> list:
            # ... логика запроса к БД
            return [{"id": 1}]
    
    class UserService:
        def __init__(self, db_client: DatabaseClient):
            self._db_client = db_client
    
        def get_user(self, user_id: int):
            return self._db_client.query(f"SELECT * FROM users WHERE id={user_id}")
    
    # Явное внедрение зависимости при создании объекта
    db = DatabaseClient()
    user_service = UserService(db_client=db)
  2. Через метод (Method/Setter Injection) Зависимость передается через отдельный метод уже после создания объекта. Менее предпочтителен, так как объект может находиться в невалидном состоянии до вызова сеттера.

    class ReportGenerator:
        _formatter = None
    
        def set_formatter(self, formatter):
            self._formatter = formatter
    
        def generate(self, data):
            if not self._formatter:
                raise ValueError("Formatter not set")
            return self._formatter.format(data)
  3. Через фабричную функцию Функция или метод, который инкапсулирует логику создания объекта и его зависимостей.

    def create_user_service() -> UserService:
        db_client = DatabaseClient()
        return UserService(db_client=db_client)
    
    # Использование
    user_service = create_user_service()

Основной принцип — инверсия контроля (IoC): класс не создает свои зависимости сам, а получает их извне. Это упрощает тестирование (можно подменить реальную БД на mock-объект) и повышает модульность кода.