fastapi yield 与上下文管理器机制
"""
第四步:yield 与上下文管理器
目标:理解 yield 把函数劈成"准备 / 清理"两半,这正是 with 语句的机制。
这是通往 async with / 异步的地基。
对应 fastapi 里的:用 yield 写的依赖(yield 前是准备,yield 后是清理),
比如 db 连接:yield 之前打开,请求结束后 yield 之后关闭。
"""
from contextlib import contextmanager
# ========== 1) 手写一个上下文管理器(类的写法) ==========
# with 语句背后就是调用对象的 __enter__ 和 __exit__ 两个方法。
class Resource:
def __init__(self, name):
self.name = name
def __enter__(self):
print(f" [__enter__] 打开 {self.name}")
return self # with ... as x,这个返回值就是 x
def __exit__(self, exc_type, exc_val, exc_tb):
# 无论 with 块里正常结束还是抛异常,__exit__ 都会被调用 —— 这是清理的保证
print(f" [__exit__] 关闭 {self.name}")
# ========== 2) 用 yield + @contextmanager 写同样的东西(更常见) ==========
@contextmanager
def resource(name):
print(f" [准备] 打开 {name}") # <- 相当于 __enter__
try:
yield name # <- yield 出去的值 = with ... as 的值
finally:
print(f" [清理] 关闭 {name}") # <- 相当于 __exit__,放 finally 保证一定执行
if __name__ == "__main__":
print("--- 类写法 ---")
with Resource("数据库") as r:
print(f" 正在使用 {r.name}")
print("\n--- yield 写法 ---")
with resource("文件") as f:
print(f" 正在使用 {f}")
print("\n--- 就算里面抛异常,清理照样执行 ---")
try:
with resource("连接") as c:
print(f" 使用 {c},然后出错了")
raise RuntimeError("boom")
except RuntimeError as e:
print(f" 捕获到异常: {e}")
输出结果:
--- 类写法 ---
[__enter__] 打开 数据库
正在使用 数据库
[__exit__] 关闭 数据库
--- yield 写法 ---
[准备] 打开 文件
正在使用 文件
[清理] 关闭 文件
--- 就算里面抛异常,清理照样执行 ---
[准备] 打开 连接
使用 连接,然后出错了
[清理] 关闭 连接
捕获到异常: boom

浙公网安备 33010602011771号