Как пользоваться DI контейнером в питоне и внедрить БД в класс?

Для начала, Dependency Injection (DI) - это паттерн проектирования, который позволяет управлять зависимостями объектов в приложении. DI контейнер - это инструмент, который облегчает внедрение зависимостей в объекты и управление их жизненным циклом.

В Python существует несколько библиотек для работы с DI, таких как injector, pycontainer, Hypodermic, или вы можете использовать встроенные инструменты, такие как модуль inspect для поиска зависимостей класса и явного их внедрения.

Давайте рассмотрим пример внедрения базы данных в класс с использованием DI контейнера. Допустим, у нас есть класс UserRepository, который зависит от объекта базы данных для выполнения операций с пользователями.

class UserRepository:
    def __init__(self, db):
        self.db = db

    def get_user(self, user_id):
        return self.db.query("SELECT * FROM users WHERE id = ?", user_id)

    def add_user(self, user_data):
        return self.db.execute("INSERT INTO users (name, email) VALUES (?, ?)", user_data['name'], user_data['email'])

Теперь давайте создадим DI контейнер и внедрим базу данных в UserRepository.

class Database:
    def __init__(self, connection_string):
        self.connection_string = connection_string

    def query(self, query, *args):
        # Реализация метода query
        pass

    def execute(self, query, *args):
        # Реализация метода execute
        pass

db = Database("sqlite:///:memory:")  # Пример подключения к SQLite базе данных

# DI контейнер
container = {
    'db': db
}

def get_instance(cls):
    dependencies = {k: container[k] for k in cls.__init__.__code__.co_varnames if k in container}
    return cls(**dependencies)

user_repository = get_instance(UserRepository)

В данном примере мы создали экземпляр базы данных db и поместили его в DI контейнер. Функция get_instance принимает класс, извлекает необходимые зависимости из контейнера и возвращает экземпляр класса с внедренными зависимостями.

Таким образом, при использовании DI контейнера мы можем изолировать зависимости класса, что упрощает тестирование, повторное использование кода и делает приложение более гибким и масштабируемым.