Python 现代开发学习笔记

(十)从重复准备资源引出依赖注入

1. 同一请求需要同一份准备结果

读取书籍要用数据库,核对用户也要用数据库。如果每个函数自己创建并关闭一次资源,准备动作会重复,收尾分散在各处。先用一个普通对象代替数据库,观察 FastAPI 如何把它交给两个函数。

# 项目文件:demos/dependency_trace.py
from typing import Annotated

from fastapi import Depends, FastAPI
from fastapi.testclient import TestClient

app = FastAPI()
events = []


def resource():
    events.append("打开")
    try:
        yield object()
    finally:
        events.append("关闭")


Resource = Annotated[object, Depends(resource)]


def identity(value: Resource):
    events.append("核对身份")
    return value


@app.get("/trace")
def trace(value: Resource, user_resource: Annotated[object, Depends(identity)]):
    events.append("执行路由")
    return {"same": value is user_resource, "events": events.copy()}


with TestClient(app) as client:
    print(client.get("/trace").json())
print(events)  # 同一请求复用 resource 的结果,finally 只执行一次。
# 观察一次请求的准备、复用、执行与收尾。
uv run python ../demos/dependency_trace.py

响应中的 same 为 True,函数执行期间的记录为 ['打开', '核对身份', '执行路由']。请求完成后,程序再输出 ['打开', '核对身份', '执行路由', '关闭']。

Depends(resource) 告诉 FastAPI:调用当前函数前,先取得 resource 的结果,填入这个参数。identity 又声明它依赖相同资源,形成依赖关系。默认情况下,同一次请求中同一项依赖的结果会被缓存,后续引用直接复用;下一条请求重新准备。

2. yield 前后的执行位置

resource 是生成器函数:先执行“打开”,在 yield 交出对象;框架执行后续函数,再恢复生成器,让 finally 收尾。定义 FastAPI 的 yield 依赖时直接写生成器函数,框架负责把它用于上下文管理。

按请求顺序展开资源依赖和身份子依赖,展示同一对象被两处复用,函数结束后恢复 yield 执行 finally,下一请求再创建新对象

本章采用 Depends(get_session) 的默认请求作用域,收尾在响应发送后进行。FastAPI 还提供 Depends(get_session, scope="function"),把收尾提前到路由处理结束、响应发送之前。流式响应可能还要继续读取资源,选择时要跟踪资源最后一次被使用的位置。本项目先形成普通 JSON 响应,写入事务已经在业务函数内提交,不把提交拖到依赖的收尾阶段。可对照 yield 依赖与 scope。

笔记正文与全部题解 · Ctrl / ⌘ K