python-dependency-injection
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePython Dependency Injection
Python Dependency Injection
Dependency Injection (DI) is a design pattern where a class receives its dependencies from an external source rather than constructing them internally. This decouples components, improves testability, and enables flexible configuration without modifying production code.
依赖注入(DI)是一种设计模式,在该模式中,类从外部源接收其依赖项,而不是在内部构造它们。这可以解耦组件,提高可测试性,并能在不修改生产代码的情况下实现灵活配置。
Core Concept: Inversion of Control
核心概念:控制反转
Inversion of Control (IoC) shifts responsibility for creating and managing dependencies from the dependent class to an external orchestrator (a container or the caller). The class declares what it needs; something else provides it.
Tight coupling (avoid):
python
class UserNotifier:
def __init__(self):
self.email = EmailService() # hard-coded, untestable
def notify(self, msg):
self.email.send(msg)Loose coupling via DI (prefer):
python
class UserNotifier:
def __init__(self, email_service: EmailService):
self.email_service = email_service # injected, swappable
def notify(self, msg):
self.email_service.send(msg)
notifier = UserNotifier(EmailService())控制反转(IoC)将创建和管理依赖项的责任从依赖类转移到外部协调器(容器或调用者)。类声明它需要什么,由其他对象提供这些依赖。
应避免的紧耦合:
python
class UserNotifier:
def __init__(self):
self.email = EmailService() # 硬编码,无法测试
def notify(self, msg):
self.email.send(msg)通过DI实现的松耦合(推荐):
python
class UserNotifier:
def __init__(self, email_service: EmailService):
self.email_service = email_service # 注入,可替换
def notify(self, msg):
self.email_service.send(msg)
notifier = UserNotifier(EmailService())Injection Styles
注入方式
| Style | How | When to use |
|---|---|---|
| Constructor | Pass via | Default; dependencies are required |
| Setter | Assign via method after construction | Optional dependencies |
| Method | Pass directly to the method call | One-off or per-call dependencies |
Constructor injection is the preferred style. It makes all dependencies explicit and visible at object creation time.
| 方式 | 实现方式 | 使用场景 |
|---|---|---|
| 构造函数注入 | 通过 | 默认方式;依赖项是必需的 |
| Setter注入 | 构造后通过方法赋值 | 可选依赖项 |
| 方法注入 | 直接传递给方法调用 | 一次性或每次调用的依赖项 |
构造函数注入是首选方式。它会在对象创建时明确显示所有依赖项。
The dependency-injector
Library
dependency-injectordependency-injector
库
dependency-injectorFor production applications, use the package (v4.x, BSD licensed, Python ≥ 3.8):
dependency-injectorbash
pip install dependency-injector对于生产应用,使用包(v4.x版本,BSD许可证,要求Python ≥ 3.8):
dependency-injectorbash
pip install dependency-injectorContainer-Provider Architecture
容器-提供者架构
The framework organizes everything around two primitives:
- Container — the central registry; declares all dependencies and how they are built
- Provider — defines how a single dependency instance is created
python
from dependency_injector import containers, providers
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,
)该框架围绕两个核心原语组织所有内容:
- Container(容器) — 中央注册表;声明所有依赖项及其构建方式
- Provider(提供者) — 定义单个依赖项实例的创建方式
python
from dependency_injector import containers, providers
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,
)Provider Types
提供者类型
| Provider | Behavior | Use case |
|---|---|---|
| Single shared instance for entire app lifetime | DB connections, API clients, caches |
| New instance on every call | Request handlers, per-operation objects |
| Reads from env vars, YAML, JSON, ini | App settings |
| Managed lifecycle with setup/teardown | DB sessions, file handles |
| Wraps any callable | Functions, class methods |
| Provides a fixed value | Constants, pre-built objects |
| Selects a provider based on config | Environment-based switching |
| 提供者 | 行为 | 使用场景 |
|---|---|---|
| 整个应用生命周期内的单个共享实例 | 数据库连接、API客户端、缓存 |
| 每次调用都创建新实例 | 请求处理器、每次操作的对象 |
| 从环境变量、YAML、JSON、ini文件读取 | 应用设置 |
| 具有设置/销毁的托管生命周期 | 数据库会话、文件句柄 |
| 包装任何可调用对象 | 函数、类方法 |
| 提供固定值 | 常量、预构建对象 |
| 根据配置选择提供者 | 基于环境的切换 |
Configuration Provider
配置提供者
Load settings from multiple sources:
python
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
container = Container()
container.config.api_key.from_env("API_KEY", required=True)
container.config.timeout.from_env("TIMEOUT", as_=int, default=5)从多个源加载设置:
python
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
container = Container()
container.config.api_key.from_env("API_KEY", required=True)
container.config.timeout.from_env("TIMEOUT", as_=int, default=5)Also supports: .from_yaml(), .from_ini(), .from_dict(), .from_pydantic()
还支持:.from_yaml(), .from_ini(), .from_dict(), .from_pydantic()
undefinedundefinedWiring: Auto-inject into Functions
依赖连接:自动注入到函数
Wiring eliminates manual dependency passing in function calls. Decorate a function with , mark parameters with , then call :
@injectProvide[...]container.wire()python
from dependency_injector.wiring import inject, Provide
@inject
def main(service: Service = Provide[Container.service]) -> None:
service.do_work()
if __name__ == "__main__":
container = Container()
container.config.from_env(...)
container.wire(modules=[__name__])
main() # service is injected automaticallyWire entire packages at once: .
container.wire(packages=["myapp"])依赖连接消除了函数调用中手动传递依赖项的操作。使用装饰函数,用标记参数,然后调用:
@injectProvide[...]container.wire()python
from dependency_injector.wiring import inject, Provide
@inject
def main(service: Service = Provide[Container.service]) -> None:
service.do_work()
if __name__ == "__main__":
container = Container()
container.config.from_env(...)
container.wire(modules=[__name__])
main() # service会被自动注入一次性连接整个包:。
container.wire(packages=["myapp"])Overriding for Tests
测试中的覆盖
Override any provider without modifying application code:
python
undefined无需修改应用代码即可覆盖任何提供者:
python
undefinedIn tests
在测试中
with container.api_client.override(mock.Mock()):
main() # the mock is injected instead
Or using the `dependency-injector` testing helpers:
```python
def test_service_behavior():
container = Container()
container.db.override(providers.Factory(FakeDB))
service = container.service()
assert service.get_data() == "expected"FastAPI equivalent via :
dependency_overridespython
app.dependency_overrides[get_db_session] = lambda: FakeDBSession()with container.api_client.override(mock.Mock()):
main() # 注入的是模拟对象而非真实实例
或者使用`dependency-injector`的测试辅助工具:
```python
def test_service_behavior():
container = Container()
container.db.override(providers.Factory(FakeDB))
service = container.service()
assert service.get_data() == "expected"FastAPI中通过实现的等效方式:
dependency_overridespython
app.dependency_overrides[get_db_session] = lambda: FakeDBSession()Resource Provider: Managed Lifecycle
Resource提供者:托管生命周期
Use for dependencies that require explicit setup and teardown:
Resourcepython
from dependency_injector import resources
class DatabaseResource(resources.Resource):
def init(self) -> Database:
db = Database(url=self.config.db_url())
db.connect()
return db
def shutdown(self, db: Database) -> None:
db.disconnect()
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
db = providers.Resource(DatabaseResource, config=config)对于需要显式设置和销毁的依赖项,使用:
Resourcepython
from dependency_injector import resources
class DatabaseResource(resources.Resource):
def init(self) -> Database:
db = Database(url=self.config.db_url())
db.connect()
return db
def shutdown(self, db: Database) -> None:
db.disconnect()
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
db = providers.Resource(DatabaseResource, config=config)Usage
使用方式
container = Container()
container.init_resources()
container = Container()
container.init_resources()
... use container.db() ...
... 使用container.db() ...
container.shutdown_resources()
undefinedcontainer.shutdown_resources()
undefinedAsync Injection
异步注入
The framework supports async resources and FastAPI's native :
Depends()python
undefined该框架支持异步资源和FastAPI原生的:
Depends()python
undefineddependency-injector async resource
dependency-injector异步资源
class AsyncDBResource(resources.AsyncResource):
async def init(self) -> AsyncDB:
db = AsyncDB()
await db.connect()
return db
async def shutdown(self, db: AsyncDB) -> None:
await db.disconnect()class AsyncDBResource(resources.AsyncResource):
async def init(self) -> AsyncDB:
db = AsyncDB()
await db.connect()
return db
async def shutdown(self, db: AsyncDB) -> None:
await db.disconnect()FastAPI native async dependency
FastAPI原生异步依赖
async def get_db() -> AsyncGenerator[AsyncSession, None]:
async with async_session() as session:
yield session
@app.get("/items")
async def read_items(db: AsyncSession = Depends(get_db)):
...
undefinedasync def get_db() -> AsyncGenerator[AsyncSession, None]:
async with async_session() as session:
yield session
@app.get("/items")
async def read_items(db: AsyncSession = Depends(get_db)):
...
undefinedBest Practices
最佳实践
Program to abstractions:
python
from abc import ABC, abstractmethod
class MessageSender(ABC):
@abstractmethod
def send(self, message: str) -> None: ...
class EmailSender(MessageSender):
def send(self, message: str) -> None:
print(f"Email: {message}")
class SMSSender(MessageSender):
def send(self, message: str) -> None:
print(f"SMS: {message}")针对抽象编程:
python
from abc import ABC, abstractmethod
class MessageSender(ABC):
@abstractmethod
def send(self, message: str) -> None: ...
class EmailSender(MessageSender):
def send(self, message: str) -> None:
print(f"Email: {message}")
class SMSSender(MessageSender):
def send(self, message: str) -> None:
print(f"SMS: {message}")Container switches implementation without changing consumer
无需修改消费者代码,容器即可切换实现
class Container(containers.DeclarativeContainer):
sender = providers.Factory(EmailSender) # swap to SMSSender anytime
**Centralize dependency configuration** — define all providers in one container module, not scattered across the codebase.
**Avoid circular dependencies** — if A depends on B and B depends on A, restructure or use a factory provider to delay instantiation.
**Use the right scope** — `Singleton` for stateless shared resources; `Factory` for stateful per-request objects. Mismatched scopes cause subtle state leakage bugs.
**Lock dependency versions** — pin exact versions in a lock file (`poetry.lock`, pip-compile output) to avoid dependency confusion attacks.class Container(containers.DeclarativeContainer):
sender = providers.Factory(EmailSender) # 随时可替换为SMSSender
**集中管理依赖配置** — 在一个容器模块中定义所有提供者,不要分散在代码库中。
**避免循环依赖** — 如果A依赖B且B依赖A,重构代码或使用工厂提供者延迟实例化。
**使用正确的作用域** — `Singleton`用于无状态共享资源;`Factory`用于有状态的每个请求对象。作用域不匹配会导致细微的状态泄漏bug。
**锁定依赖版本** — 在锁定文件(`poetry.lock`、pip-compile输出)中固定精确版本,以避免依赖混淆攻击。Anti-Patterns to Avoid
应避免的反模式
| Anti-pattern | Problem | Fix |
|---|---|---|
| Service Locator | | Inject explicitly via constructor or |
| Over-injection | 10+ constructor params | Split into smaller, focused classes |
| Tight coupling | | Accept dependency as parameter |
| Scope mismanagement | | Use |
| Monkey-patching in tests | | Use |
| 反模式 | 问题 | 修复方案 |
|---|---|---|
| 服务定位器 | 业务逻辑中的 | 通过构造函数或 |
| 过度注入 | 构造函数有10+个参数 | 拆分为更小、职责单一的类 |
| 紧耦合 | | 接受依赖项作为参数 |
| 作用域管理不当 | | 使用 |
| 测试中的猴子补丁 | | 使用 |
Quick Reference
快速参考
bash
pip install dependency-injector # base
pip install "dependency-injector[yaml]" # + YAML config support
pip install "dependency-injector[pydantic2]" # + Pydantic v2 settingspython
undefinedbash
pip install dependency-injector # 基础安装
pip install "dependency-injector[yaml]" # + YAML配置支持
pip install "dependency-injector[pydantic2]" # + Pydantic v2设置支持python
undefinedMinimal working container
最小可用容器
from dependency_injector import containers, providers
from dependency_injector.wiring import inject, Provide
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
service = providers.Factory(MyService, setting=config.setting)
@inject
def handler(svc: MyService = Provide[Container.service]):
svc.run()
container = Container()
container.config.from_dict({"setting": "value"})
container.wire(modules=[name])
handler()
undefinedfrom dependency_injector import containers, providers
from dependency_injector.wiring import inject, Provide
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
service = providers.Factory(MyService, setting=config.setting)
@inject
def handler(svc: MyService = Provide[Container.service]):
svc.run()
container = Container()
container.config.from_dict({"setting": "value"})
container.wire(modules=[name])
handler()
undefinedAdditional Resources
额外资源
For detailed coverage of advanced topics, consult:
- — Scoped dependencies, multiple containers, selector providers, and testing patterns
references/patterns.md - — Step-by-step integration with Flask, Django, and FastAPI including wiring setup
references/framework-integration.md
如需深入了解高级主题,请参考:
- — 作用域依赖、多容器、选择器提供者和测试模式
references/patterns.md - — 与Flask、Django和FastAPI的分步集成指南,包括依赖连接设置
references/framework-integration.md