python-dependency-injection

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Python 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

注入方式

StyleHowWhen to use
ConstructorPass via
__init__
Default; dependencies are required
SetterAssign via method after constructionOptional dependencies
MethodPass directly to the method callOne-off or per-call dependencies
Constructor injection is the preferred style. It makes all dependencies explicit and visible at object creation time.
方式实现方式使用场景
构造函数注入通过
__init__
传递
默认方式;依赖项是必需的
Setter注入构造后通过方法赋值可选依赖项
方法注入直接传递给方法调用一次性或每次调用的依赖项
构造函数注入是首选方式。它会在对象创建时明确显示所有依赖项。

The
dependency-injector
Library

dependency-injector

For production applications, use the
dependency-injector
package (v4.x, BSD licensed, Python ≥ 3.8):
bash
pip install dependency-injector
对于生产应用,使用
dependency-injector
包(v4.x版本,BSD许可证,要求Python ≥ 3.8):
bash
pip install dependency-injector

Container-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

提供者类型

ProviderBehaviorUse case
Singleton
Single shared instance for entire app lifetimeDB connections, API clients, caches
Factory
New instance on every callRequest handlers, per-operation objects
Configuration
Reads from env vars, YAML, JSON, iniApp settings
Resource
Managed lifecycle with setup/teardownDB sessions, file handles
Callable
Wraps any callableFunctions, class methods
Object
Provides a fixed valueConstants, pre-built objects
Selector
Selects a provider based on configEnvironment-based switching
提供者行为使用场景
Singleton
整个应用生命周期内的单个共享实例数据库连接、API客户端、缓存
Factory
每次调用都创建新实例请求处理器、每次操作的对象
Configuration
从环境变量、YAML、JSON、ini文件读取应用设置
Resource
具有设置/销毁的托管生命周期数据库会话、文件句柄
Callable
包装任何可调用对象函数、类方法
Object
提供固定值常量、预构建对象
Selector
根据配置选择提供者基于环境的切换

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()

undefined
undefined

Wiring: Auto-inject into Functions

依赖连接:自动注入到函数

Wiring eliminates manual dependency passing in function calls. Decorate a function with
@inject
, mark parameters with
Provide[...]
, then call
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 automatically
Wire entire packages at once:
container.wire(packages=["myapp"])
.
依赖连接消除了函数调用中手动传递依赖项的操作。使用
@inject
装饰函数,用
Provide[...]
标记参数,然后调用
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
undefined

In 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_overrides
:
python
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_overrides
实现的等效方式:
python
app.dependency_overrides[get_db_session] = lambda: FakeDBSession()

Resource Provider: Managed Lifecycle

Resource提供者:托管生命周期

Use
Resource
for dependencies that require explicit setup and teardown:
python
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)
对于需要显式设置和销毁的依赖项,使用
Resource
python
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()
undefined
container.shutdown_resources()
undefined

Async Injection

异步注入

The framework supports async resources and FastAPI's native
Depends()
:
python
undefined
该框架支持异步资源和FastAPI原生的
Depends()
python
undefined

dependency-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)): ...
undefined
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)): ...
undefined

Best 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-patternProblemFix
Service Locator
container.get(Service)
inside business logic hides dependencies
Inject explicitly via constructor or
@inject
Over-injection10+ constructor paramsSplit into smaller, focused classes
Tight coupling
self.dep = ConcreteClass()
inside
__init__
Accept dependency as parameter
Scope mismanagement
Singleton
wrapping a stateful request-scoped object
Use
Factory
or
Resource
with correct scope
Monkey-patching in tests
module.SomeClass = MockClass
Use
container.override()
or
dependency_overrides
反模式问题修复方案
服务定位器业务逻辑中的
container.get(Service)
会隐藏依赖项
通过构造函数或
@inject
显式注入
过度注入构造函数有10+个参数拆分为更小、职责单一的类
紧耦合
__init__
内部的
self.dep = ConcreteClass()
接受依赖项作为参数
作用域管理不当
Singleton
包装有状态的请求作用域对象
使用
Factory
或具有正确作用域的
Resource
测试中的猴子补丁
module.SomeClass = MockClass
使用
container.override()
dependency_overrides

Quick Reference

快速参考

bash
pip install dependency-injector          # base
pip install "dependency-injector[yaml]"  # + YAML config support
pip install "dependency-injector[pydantic2]"  # + Pydantic v2 settings
python
undefined
bash
pip install dependency-injector          # 基础安装
pip install "dependency-injector[yaml]"  # + YAML配置支持
pip install "dependency-injector[pydantic2]"  # + Pydantic v2设置支持
python
undefined

Minimal 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()
undefined
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()
undefined

Additional Resources

额外资源

For detailed coverage of advanced topics, consult:
  • references/patterns.md
    — Scoped dependencies, multiple containers, selector providers, and testing patterns
  • references/framework-integration.md
    — Step-by-step integration with Flask, Django, and FastAPI including wiring setup
如需深入了解高级主题,请参考:
  • references/patterns.md
    — 作用域依赖、多容器、选择器提供者和测试模式
  • references/framework-integration.md
    — 与Flask、Django和FastAPI的分步集成指南,包括依赖连接设置