返回资讯中心
外部精选
软件工程
#开发者实时流

Python 依赖注入库 cdi 发布,主打简化和速度

来源标题:Python Depedency Injection Library

Python 依赖注入库 cdi 发布,旨在简化依赖注入并提升运行速度。该库支持 forward reference、union、Lazy type、Contextvar、泛型等特性,作者声明库代码不使用 AI,AI 生成的代码 PR 将被拒绝。

Hacker News 最新disel2026/11/1 · 昨天20 分钟阅读

以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。

我猜 cdi 大概代表“cute dependency injector”(可爱的依赖注入器)吧(?) pip install cdi-di AI 声明 本库代码的任何部分都未使用 AI,将来也不会使用;由 AI 生成的 PR 看起来就像 AI 生成的代码(低质量),这类 PR 会被拒绝,只接受 AI 生成的文档类 PR 有些 Python 依赖注入器需要一些配置,并且会让简单的依赖注入变得更难理解,而 cdi 的目标是简化依赖注入并保持快速(相对 Python 而言) 前向引用解析 联合类型支持 用于循环依赖的 Lazy 类型 Contextvar 支持 用于非单例实例的 Transient 泛型/TypeVar 支持 受限实例 作用域继承 不支持可注入的默认实现 显式优于隐式 本库试图让你在类型标注上保持显式,同时不牺牲可读性和易用性 import cdi

这个容器会包含它自己注册的类型

ctr = cdi.Container()

把此函数注册为 int 类型的工厂

def get_int() -> int: return 100

把 Foo 注册为可注入类型

这样我们就能创建实例

class Foo: def init(self, number: int) -> None: self.number = number

创建一个作用域,它可以访问 ctr 中注册的类型

然后获取 Foo 的实例

scope = cdi.Scope(cdi) instance = scope.get_instance(Foo) assert instance.number == 100 import cdi from typing import Generic, TypeVar from collection.abc import Sequence T = TypeVar('T') ctr = cdi.Container() class MyBase(Generic[T]): def init(self, field: T) -> None: self.field = field class MyType(MyBase[str]): pass def name_generator() -> str: return "foo" scope = cdi.Scope(name, container=ctr) instance = scope.get_instance(MyType) assert instance.field == "foo" ctr = cdi.Container() class Foo(Generic[T]): def init(self, v: T): self.v = v cdi.Injectable(ctr).register("hello world") cdi.Injectable(ctr).register(100) scope = Scope(name, container=ctr) assert scope.get_instance(Foo[int]).v == 100 assert scope.get_instance(Foo[str]).v == "hello world"

甚至支持嵌套

assert scope.get_instance(Foo[Foo[int]]).v.v == 100 scope = cdi.Scope(name, container=ctr) with scope.lifetime() as lifetime:

绑定到 lifetime 作用域,但对于非新实例,

它们会从 scope 中获取

foo = scope.get_instance(Foo[int]) assert not scope.has_instance(Foo[int]) scope = cdi.Scope(name, container=ctr) class A: def init(self, b: B) -> None: self.b = b class B: def init(self, a: cdi.Lazy[A]) -> None: self.a = a ctr.update_forward_refs(sys.modules[name]) a = scope.get_instance(A) b = scope.get_instance(B) assert a.b.a.wake() is a 不支持的功能 列表值 将 Typevar 作为可注入的返回类型 异步 尽管 cdi 没有显式的异步接口,但它支持异步代码;cdi 没有内部 IO(不会让出控制权),所以为那些完全不会从中受益的代码实现异步接口没有意义 cdi 支持运行多个 asyncio executor,因为每个 executor 都是独立的线程,并且 cdi 对内部对象持有内部 RLock,以防这些对象跨线程共享 Container 容器包含已注册的类型及其工厂,类型通过 cdi.Injectable 注册到容器,cdi.Injectable 会在容器中创建相应的内部工厂 如果某个类型没有在容器中注册,cdi.Scope 不会尝试创建该类型,而是抛出错误,除非向作用域显式提供了 EvaluateUnknownTypesPolicy,显式优于隐式 ctr = cdi.Container() 你可以通过调用 cdi.Container.has_registered 来检查某个类型是否已注册到容器 有些类型的返回类型或参数中可能含有未解析的前向引用,在求值时无法知道这些前向引用字符串背后是什么类型 class Foo:

Boo 是什么类型?我们只看到一个字符串

def init(self, boo: 'Boo') -> None: ... 这样的工厂将无法用于注入,为了解析前向引用,容器类提供了 cid.Container.update_forward_refs,它接收你想要更新前向引用的模块,这一设计灵感来自 Pydantic/v1 update_forward_refs 必须在存在能够求值前向引用名称的类之后调用 import sys class Foo:

引用了尚未定义的 Boo

def init(self, boo: 'Boo') -> None: ... class Boo: ...

现在 Boo 已定义,我们可以更新当前模块中的工厂

ctr.update_forward_refs(sys.modules[name])

正常运行

instance = Scope(name, container=ctr).get_instance(Foo) Injectable Injectable 负责把你的类型注册到容器中,注入器会为提供的类型创建一个内部 Factory,并将其注册到绑定的容器中 ctr = cdi.Container() injector = cdi.Injectable(ctr) injector.register(Foo) injector.register(my_func) 它也可以用作装饰器 ctr = cdi.Container() class Foo: ... def my_func() -> int: ... 创建工厂时,注入器依赖提供的类型提示 注册类时,依赖项取自类的 init 签名,而工厂实现(调用后返回该类型)使用类的 call 对于函数,函数签名将用于确定参数和返回类型,调用工厂时最终会调用提供的函数 常量可以注入到容器中,常量的类型将作为工厂的返回类型,所有需要该类型的作用域都会求值为该常量,充当容器中的“全局变量” 例如 ctr = cdi.Container() cdi.Injectable().register("hello world") scope = cdi.Scope(name, container=ctr) assert scope.get_instance(str) == "hello world" Scopes 作用域定义了实例的生命周期或边界,Scope 只包含实例,并且绑定到一个 cdi.Container

(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →