Python 依赖注入框架:`injector` / ` zależności` 的深度应用

好的,各位朋友,欢迎来到今天的“Python依赖注入框架:injector / zależności 深度应用”讲座。今天咱们不搞虚头巴脑的,直接上手,把依赖注入这玩意儿,尤其是 injectorzależności 这两个框架,扒个精光!

开场白:告别意大利面条式代码

咱们先来说说,为啥要搞依赖注入?想象一下,你写了一个超级大的程序,里面各种类互相依赖,改动一个地方,整个程序都得跟着颤抖。这就是典型的“意大利面条式代码”,缠绕在一起,剪不断,理还乱。

依赖注入就像一剂良药,能把这些缠绕在一起的依赖关系解开,让你的代码更灵活、更易于测试和维护。

什么是依赖注入?

说白了,依赖注入就是把一个类需要的依赖,不是在类内部创建,而是“注入”进去。想象一下,你开了一家咖啡馆,需要牛奶,你不是自己养奶牛(内部创建),而是从牛奶供应商那里拿(注入)。

这样做的好处显而易见:

  • 解耦: 类不再负责创建自己的依赖,依赖关系更加清晰。
  • 可测试性: 你可以很容易地替换掉依赖,比如用一个假的牛奶供应商来测试咖啡馆的功能。
  • 可维护性: 修改依赖的实现,不会影响到使用该依赖的类。

injector:老牌劲旅,功能强大

injector 是一个比较成熟的 Python 依赖注入框架,用起来很方便。

1. 安装 injector

pip install injector

2. 定义接口和实现

咱们来个简单的例子,定义一个问候语接口和一个实现:

import abc
from injector import inject

class Greeter(abc.ABC):
    @abc.abstractmethod
    def greet(self, name: str) -> str:
        pass

class EnglishGreeter(Greeter):
    def greet(self, name: str) -> str:
        return f"Hello, {name}!"

class SpanishGreeter(Greeter):
    def greet(self, name: str) -> str:
        return f"Hola, {name}!"

3. 使用 injector 配置依赖关系

from injector import Injector, Module, provider

class GreeterModule(Module):
    def configure(self, binder):
        binder.bind(Greeter, to=EnglishGreeter)

    # 或者使用 Provider 方法
    # @provider
    # def provide_greeter(self) -> Greeter:
    #     return EnglishGreeter()

if __name__ == '__main__':
    injector = Injector([GreeterModule()])
    greeter = injector.get(Greeter)
    print(greeter.greet("Alice"))  # 输出: Hello, Alice!

解释:

  • Greeter 是一个接口,定义了 greet 方法。
  • EnglishGreeterSpanishGreeterGreeter 接口的两种实现。
  • GreeterModule 是一个配置模块,告诉 injector 如何创建 Greeter 的实例。binder.bind(Greeter, to=EnglishGreeter) 表示将 Greeter 接口绑定到 EnglishGreeter 实现。
  • injector.get(Greeter)injector 获取 Greeter 的实例,实际上得到的是 EnglishGreeter 的实例。

4. 在类中使用依赖注入

class GreetingService:
    @inject
    def __init__(self, greeter: Greeter):
        self.greeter = greeter

    def say_hello(self, name: str) -> str:
        return self.greeter.greet(name)

if __name__ == '__main__':
    injector = Injector([GreeterModule()])
    greeting_service = injector.get(GreetingService)
    print(greeting_service.say_hello("Bob"))  # 输出: Hello, Bob!

解释:

  • GreetingService 类依赖于 Greeter 接口。
  • @inject 装饰器告诉 injector,在创建 GreetingService 实例时,自动注入 Greeter 类型的依赖。
  • injector.get(GreetingService)injector 获取 GreetingService 的实例,injector 会自动创建 Greeter 的实例并注入到 GreetingService 中。

5. 使用 Provider 方法

Provider方法是一种更加灵活的依赖提供方式,允许你自定义依赖的创建逻辑。

from injector import Injector, Module, provider

class Config:
    def __init__(self, language: str):
        self.language = language

class Greeter:
    def __init__(self, language: str):
        self.language = language

    def greet(self, name: str) -> str:
        if self.language == "en":
            return f"Hello, {name}!"
        elif self.language == "es":
            return f"Hola, {name}!"
        else:
            return f"Greeting, {name}!"

class GreeterModule(Module):
    @provider
    def provide_config(self) -> Config:
        return Config(language="en")

    @provider
    def provide_greeter(self, config: Config) -> Greeter:
        return Greeter(language=config.language)

class GreetingService:
    @inject
    def __init__(self, greeter: Greeter):
        self.greeter = greeter

    def say_hello(self, name: str) -> str:
        return self.greeter.greet(name)

if __name__ == '__main__':
    injector = Injector([GreeterModule()])
    greeting_service = injector.get(GreetingService)
    print(greeting_service.say_hello("Bob"))  # 输出: Hello, Bob!

解释:

  • provide_config 方法创建 Config 实例,并将其作为依赖提供。
  • provide_greeter 方法接收 Config 实例作为参数,并根据 Config 中的语言设置创建 Greeter 实例。

zależności:现代框架,简洁优雅

zależności 是一个相对较新的 Python 依赖注入框架,它的设计目标是简单易用,代码更简洁。

1. 安装 zależności

pip install zależności

2. 定义接口和实现 (和 injector 类似)

import abc

class Greeter(abc.ABC):
    @abc.abstractmethod
    def greet(self, name: str) -> str:
        pass

class EnglishGreeter(Greeter):
    def greet(self, name: str) -> str:
        return f"Hello, {name}!"

class SpanishGreeter(Greeter):
    def greet(self, name: str) -> str:
        return f"Hola, {name}!"

3. 使用 zależności 配置依赖关系

from zależności import inject, Container

container = Container()
container.register(Greeter, EnglishGreeter)

@inject
def greet_someone(greeter: Greeter):
    return greeter.greet("Alice")

if __name__ == '__main__':
    print(greet_someone())  # 输出: Hello, Alice!

解释:

  • Containerzależności 的核心类,用于管理依赖关系。
  • container.register(Greeter, EnglishGreeter)Greeter 接口注册到 EnglishGreeter 实现。
  • @inject 装饰器会自动从 container 中获取 Greeter 的实例并注入到 greet_someone 函数中。

4. 在类中使用依赖注入

from zależności import inject, Container

container = Container()
container.register(Greeter, EnglishGreeter)

class GreetingService:
    @inject
    def __init__(self, greeter: Greeter):
        self.greeter = greeter

    def say_hello(self, name: str) -> str:
        return self.greeter.greet(name)

if __name__ == '__main__':
    greeting_service = container.resolve(GreetingService)
    print(greeting_service.say_hello("Bob"))  # 输出: Hello, Bob!

解释:

  • container.resolve(GreetingService)container 获取 GreetingService 的实例,zależności 会自动创建 Greeter 的实例并注入到 GreetingService 中。

5. 使用工厂函数

zależności 也支持使用工厂函数来创建依赖。

from zależności import inject, Container

container = Container()

def create_greeter(language: str):
    if language == "en":
        return EnglishGreeter()
    elif language == "es":
        return SpanishGreeter()
    else:
        return EnglishGreeter() # 默认英语

container.register(Greeter, lambda: create_greeter("en"))

class GreetingService:
    @inject
    def __init__(self, greeter: Greeter):
        self.greeter = greeter

    def say_hello(self, name: str) -> str:
        return self.greeter.greet(name)

if __name__ == '__main__':
    greeting_service = container.resolve(GreetingService)
    print(greeting_service.say_hello("Bob"))

injector vs. zależności:对比分析

特性 injector zależności
易用性 稍微复杂,需要定义 Module 简洁,直接使用 Container
功能 功能强大,支持多种绑定方式,作用域等 功能较少,但满足基本需求
社区支持 活跃,文档完善 相对较新,社区支持较少
代码风格 基于类的配置 函数式配置,更简洁
学习曲线 较陡峭 较平缓

选择哪个框架?

  • injector 如果你需要更强大的功能,例如作用域管理、AOP 等,并且不介意稍微复杂一点的配置,那么 injector 是一个不错的选择。
  • zależności 如果你追求简洁易用,只需要基本的依赖注入功能,那么 zależności 会让你感到非常舒服。

高级技巧:作用域 (Scopes) 和 AOP (Aspect-Oriented Programming)

虽然咱们主要讲的是基本用法,但稍微提一下 injector 的两个高级特性:作用域和 AOP。

  • 作用域: 作用域控制依赖实例的生命周期。例如,你可以设置一个依赖在整个应用程序中只创建一个实例(单例),或者在每次请求时创建一个新的实例。
  • AOP: AOP 允许你在不修改原有代码的情况下,为代码添加额外的功能,例如日志记录、性能监控等。

真实项目中的应用

依赖注入在大型项目中非常有用。以下是一些常见的应用场景:

  • 数据库连接: 将数据库连接对象注入到需要访问数据库的类中。
  • 配置管理: 将配置对象注入到需要访问配置信息的类中。
  • 日志记录: 将日志记录器注入到需要记录日志的类中。
  • 测试: 在单元测试中,可以使用 mock 对象替换真实的依赖,以便更好地测试代码。

实际案例代码

假设我们有一个简单的电商网站,需要处理用户订单。我们需要一个 OrderProcessor 类来处理订单,它依赖于 PaymentGatewayInventoryService

不使用依赖注入的代码:

class PaymentGateway:
    def process_payment(self, amount):
        # ... 处理支付逻辑 ...
        return True

class InventoryService:
    def update_inventory(self, product_id, quantity):
        # ... 更新库存逻辑 ...
        pass

class OrderProcessor:
    def __init__(self):
        self.payment_gateway = PaymentGateway()
        self.inventory_service = InventoryService()

    def process_order(self, order):
        if self.payment_gateway.process_payment(order.total_amount):
            for item in order.items:
                self.inventory_service.update_inventory(item.product_id, item.quantity)
            return True
        else:
            return False

使用 injector 的代码:

from injector import inject, Injector, Module

class PaymentGateway:
    def process_payment(self, amount):
        # ... 处理支付逻辑 ...
        return True

class InventoryService:
    def update_inventory(self, product_id, quantity):
        # ... 更新库存逻辑 ...
        pass

class OrderProcessor:
    @inject
    def __init__(self, payment_gateway: PaymentGateway, inventory_service: InventoryService):
        self.payment_gateway = payment_gateway
        self.inventory_service = inventory_service

    def process_order(self, order):
        if self.payment_gateway.process_payment(order.total_amount):
            for item in order.items:
                self.inventory_service.update_inventory(item.product_id, item.quantity)
            return True
        else:
            return False

class AppModule(Module):
    def configure(self, binder):
        binder.bind(PaymentGateway, PaymentGateway)
        binder.bind(InventoryService, InventoryService)

if __name__ == '__main__':
    injector = Injector([AppModule()])
    order_processor = injector.get(OrderProcessor)
    # ... 创建订单对象 ...
    # order_processor.process_order(order)

使用 zależności 的代码:

from zależności import inject, Container

class PaymentGateway:
    def process_payment(self, amount):
        # ... 处理支付逻辑 ...
        return True

class InventoryService:
    def update_inventory(self, product_id, quantity):
        # ... 更新库存逻辑 ...
        pass

class OrderProcessor:
    @inject
    def __init__(self, payment_gateway: PaymentGateway, inventory_service: InventoryService):
        self.payment_gateway = payment_gateway
        self.inventory_service = inventory_service

    def process_order(self, order):
        if self.payment_gateway.process_payment(order.total_amount):
            for item in order.items:
                self.inventory_service.update_inventory(item.product_id, item.quantity)
            return True
        else:
            return False

container = Container()
container.register(PaymentGateway, PaymentGateway)
container.register(InventoryService, InventoryService)

if __name__ == '__main__':
    order_processor = container.resolve(OrderProcessor)
    # ... 创建订单对象 ...
    # order_processor.process_order(order)

可以看到,使用了依赖注入后,OrderProcessor 类不再负责创建 PaymentGatewayInventoryService 的实例,而是通过构造函数注入。这样,我们可以很容易地替换 PaymentGatewayInventoryService 的实现,例如在测试中使用 mock 对象。

总结:拥抱依赖注入,告别“面条代码”

依赖注入是一种强大的设计模式,可以帮助你编写更灵活、更易于测试和维护的代码。injectorzależności 都是优秀的 Python 依赖注入框架,选择哪个取决于你的具体需求和偏好。

记住,不要害怕学习新的东西,拥抱依赖注入,让你的代码更加优雅和健壮!

今天的讲座就到这里,谢谢大家!希望大家有所收获!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注