Skip to content

依赖注入与工厂

FastAPI DI 核心、yield 依赖、pydantic-settings、依赖覆盖、函数工厂。

Updated View as Markdown
For humans

依赖注入与工厂

依赖注入(DI)的本意:依赖的创建和使用分离,通过外部传入。FastAPI 把 DI 做成了框架核心,理解它等于理解 FastAPI 的一半。工厂模式在 Python 里则简化为函数。

为什么需要 DI

# 反面: 内部自己创建, 耦合具体实现, 难测试
def handler():
    db = MySQLConnection()   # 换库要改代码, 测试要连真库

# 正面: 依赖从参数进来, 可替换
def handler(db: DBConnection):
    ...

收益:解耦(依赖抽象)、可测试(注入 mock)、可配置(换实现不改调用方)。

FastAPI 的 DI 核心

依赖树: 嵌套解析, 自动注入

机制要点:

  • Depends() 声明依赖,FastAPI 递归解析依赖树,自动注入参数
  • 依赖可以是函数、类、可调用对象;返回值就是注入值
  • yield 依赖yield 前的代码在请求开始执行,yield 后在请求结束执行(清理数据库会话、关闭连接)
  • 依赖可以依赖依赖(嵌套),形成依赖树,FastAPI 处理缓存和生命周期

依赖覆盖:测试杀手锏

app.dependency_overrides 在不改业务代码的情况下替换依赖,是 FastAPI 测试的根基:

def fake_db():
    yield MemoryDB()

app.dependency_overrides[get_db] = fake_db  # 测试里替换

with TestClient(app) as client:
    ...

不用 mock 框架,不用改代码,把“连数据库的依赖”换成“内存实现”即可。这体现了 DI 的核心价值:替换依赖而不动调用方

工厂模式:函数即工厂

Python 的工厂不需要工厂类:

def make_client(env: str) -> Client:      # 简单工厂
    if env == "prod":
        return ProdClient()
    return DevClient()

make_prod = lambda: make_client("prod")   # 带参工厂: 闭包/partial 固化参数
  • 简单工厂 = 一个函数,根据参数返回不同对象
  • 工厂方法 = 函数内部多态创建(配 Protocol)
  • 抽象工厂 = 返回一组相关对象的函数,业务中少见,别硬造

pydantic-settings

配置管理是 DI 的常见落点:

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    database_url: str
    debug: bool = False

@lru_cache
def get_settings() -> Settings:
    return Settings()   # 从环境变量/ .env 读取
  • 配置即依赖:通过 Depends(get_settings) 注入到需要的地方,而不是到处 import
  • @lru_cache 保证进程内只读一次(单例作用域,见单例篇)
  • 环境变量自动映射,测试时改环境变量即可换配置

面试追问

  1. DI 解决什么? 依赖创建和使用分离。解耦、可测试(注入 mock)、可配置
  2. FastAPI 怎么实现 DI? Depends 声明依赖,框架递归解析依赖树自动注入。yield 依赖处理资源清理
  3. dependency_overrides 干什么? 测试时替换依赖,不改业务代码。FastAPI 测试的核心机制
  4. Python 工厂怎么写? 函数返回对象。带参工厂用闭包或 functools.partial,不需要工厂类
  5. pydantic-settings 的作用? 配置类从环境变量读取,lru_cache 保证单例,通过 Depends 注入。测试换环境变量即换配置
Navigation

Type to search…

↑↓ navigate↵ selectEsc close