使用Dependency Injector
可以将应用程序结构保存在一个地方。这个地方叫做集装箱。您可以使用容器来管理应用程序的所有组件。所有组件依赖关系都是显式定义的。这提供了对应用程序结构的控制。它很容易理解和改变。
容器就像应用程序的映射。你总是知道什么取决于什么
什么是依赖注入,有什么好处?
依赖注入是一种强大的设计模式,可以帮助使软件更加模块化、更灵活、更容易测试。这种设计思想可以实现低耦合,高内聚。
我的现在的理解就是你先写好一个函数结构,但参数什么的不写死在里面,而是通过依赖注入后面再传入进来(记时的理解,不一定对)
设计案例:
我们之前的写法:
import os
class ApiClient:
def __init__(self) -> None:
self.api_key = os.getenv("API_KEY") # <-- dependency
self.timeout = int(os.getenv("TIMEOUT")) # <-- dependency
class Service:
def __init__(self) -> None:
self.api_client = ApiClient() # <-- dependency
def main() -> None:
service = Service() # <-- dependency
...
if __name__ == "__main__":
main()
依赖注入的写法:(通过函数传参方式)
import os
class ApiClient:
def __init__(self, api_key: str, timeout: int) -> None:
self.api_key = api_key # <-- dependency is injected
self.timeout = timeout # <-- dependency is injected
class Service:
def __init__(self, api_client: ApiClient) -> None:
self.api_client = api_client # <-- dependency is injected
def main(service: Service) -> None: # <-- dependency is injected
...
if __name__ == "__main__":
main(
service=Service(
api_client=ApiClient(
api_key=os.getenv("API_KEY"),
timeout=int(os.getenv("TIMEOUT")),
),
),
)
但是这种方式实现的话,函数调用时需要创建很多对象,不便于管理和维护。
这个时候就需要依赖注入器(dependency_injector 框架)来帮助组建对象和函数。大概形式如下所示:
from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
api_client = providers.Singleton(
ApiClient,
api_key=config.api_key,
timeout=config.timeout,
)
service = providers.Factory(
Service,
api_client=api_client,
)
@inject
def main(service: Service = Provide[Container.service]) -> None:
print('main',service.api_client.api_key)
if __name__ == "__main__":
container = Container()
container.config.api_key.from_env("API_KEY", default='api_key', required=True)
container.config.timeout.from_env("TIMEOUT", as_=int, default=5)
container.wire(modules=[__name__])
main() # <-- dependency is injected automatically
with container.api_client.override(mock.Mock()):
main() # <-- overridden dependency is injected automatically
这样实现有几个好处:
- 依赖自动组装和注入
- 可以通过container.api_client.override(mock.Mock()) 使用测试函数
- 可以方便配置多套不同的开发环境的依赖注入。
- 函数的结构更加显示,容易看出入参的结构。
dependency_injector框架主要构成
工具集 提供的方法/类 备注
Providers
Factory, Singleton, Callable, Coroutine, Object, List, Dict, Configuration, Resource, Dependency等方法
- 帮助组装对象;
- 方便Overriding覆盖
Overriding
container.api_client_factory.override
container.api_client_factory.reset_override
覆盖对象改变注入
Configuration 提供了从多种源读取配置:yaml、ini、json、env等
Resource 提供一种类似Singleton的资源加载方式,提供初始化,和卸载资源方法。
container 容器,里面定义了多个实例
Wiring 提供@inject装饰器,指示需要被装饰的函数