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
posted @ 2026-07-03 12:44  RolandHe  阅读(2)  评论(0)    收藏  举报