(七)文件的 with 与生成器式上下文
1. 转换失败后文件仍然关闭
# 整数转换出错时先离开文件上下文,再进入外面的 except。
from pathlib import Path
path = Path("bad-minutes.txt")
path.write_text("很多\n", encoding="utf-8")
try:
with path.open(encoding="utf-8") as stream:
minutes = int(stream.readline())
except ValueError:
print("文件关闭", stream.closed)得到 文件关闭 True。with 把文件使用范围与关闭动作放在同一层缩进关系中;文件打开本身失败时,主体没有开始,外层则会收到相应的打开错误。文件读写
2. 用 contextmanager 包装同一套进入退出动作
上一章手写了 __enter__ 和 __exit__。标准库还提供了把一个生成器函数变成上下文管理器的装饰器:
# yield 前建立资源,yield 交出资源,finally 在离开上下文时关闭它。
from contextlib import contextmanager
from pathlib import Path
@contextmanager
def open_utf8(path):
stream = path.open(encoding="utf-8")
print("打开")
try:
yield stream
finally:
stream.close()
print("关闭")
path = Path("context.txt")
path.write_text("Python", encoding="utf-8")
with open_utf8(path) as stream:
print(stream.read())输出“打开”“Python”“关闭”。这次 yield 用来交出上下文主体使用的对象:进入时运行到这里并暂停,离开时恢复,完成 finally。主体的异常也会被送回这个暂停位置,再经过收尾向外传播。
这里的上下文生成器交出一次资源,主体只运行一次;它与前一章可连续交出很多记录的数据生成器承担不同任务。contextmanager