Python 现代开发学习笔记

(十五)用上下文管理器安排进入与退出

1. with、__enter__ 与 __exit__

程序使用文件、数据库连接等资源时,需要把“开始使用”和“结束处理”接起来。先用一个连接标志模拟这个过程,观察调用顺序:

# 这里用 opened 模拟连接状态,暂时不创建真实的外部连接。
class ReadingSession:
    def __init__(self):
        self.opened = False

    def __enter__(self):
        self.opened = True
        print("进入")
        return self

    def __exit__(self, exc_type, exc, traceback):
        self.opened = False
        print("退出", "正常" if exc_type is None else exc_type.__name__)
        return False


with ReadingSession() as session:
    print(session.opened)
print(session.opened)

输出“进入”、True、“退出 正常”、False。with 取得上下文管理器,先调用 __enter__,把其返回值交给 as 后的名称。本例返回 self,所以 session 指向这个 ReadingSession 实例;主体运行结束后再调用 __exit__。

正常退出时,__exit__ 收到的三个异常参数都是 None。它在这里把连接标志改回关闭状态。离开 with 不会删除 session 这个名称,最后一行仍能读取对象,只是资源已经完成退出处理。

2. 主体发生错误时先退出,再向外传递

# 主体抛出 RuntimeError,退出方法仍会处理状态,再由外层接收错误。
class ReadingSession:
    def __init__(self):
        self.opened = False

    def __enter__(self):
        self.opened = True
        print("进入")
        return self

    def __exit__(self, exc_type, exc, traceback):
        self.opened = False
        print("退出", "正常" if exc_type is None else exc_type.__name__)
        return False


try:
    with ReadingSession() as session:
        print("处理记录")
        raise RuntimeError("读取失败")
except RuntimeError as error:
    print("外层收到", error)
print(session.opened)

输出“进入”“处理记录”“退出 RuntimeError”“外层收到 读取失败”,最后是 False。RuntimeError 是这里主动发出的运行错误;异常发生后,主体后续语句停止,__exit__ 接收异常类型、异常对象和追踪信息,再把状态关闭。

正常返回与异常离开时的上下文退出过程

返回 False 使异常继续传给外层处理;返回真值则表示当前管理器接收并抑制了主体的异常。普通的资源关闭只负责完成清理,可以返回 False 或不显式返回;不要为了退出处理顺利显示,就把原本的错误一起隐藏。下一章会把这个顺序接到真实文件的读写和关闭上。with 语句

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