历史 — 在Python中,管理资源(文件、连接、事务)使用基于上下文管理器协议(enter, exit)的with结构。对于简单情况,编写整个类显得冗余,因此提出了@contextmanager装饰器(contextlib模块),允许将资源管理器定义为生成器。
问题 — 手动释放或关闭资源会很麻烦,代码会变得不稳定(例如,忘记关闭文件)。此外,我们不想为了简单的事情(例如,临时更改目录或stdout)编写一个有两个方法的单独类。
解决方案 — 使用@contextmanager简洁地描述资源使用的“开始”和“结束”,保证处理异常和释放资源。
代码示例:
from contextlib import contextmanager @contextmanager def open_file(filename, mode): f = open(filename, mode) try: yield f finally: f.close() with open_file('test.txt', 'w') as f: f.write('Hello')
关键特点:
可以让@contextmanager的yield返回多个对象(例如通过元组)吗?
可以,并且很方便返回一组相关的资源。
代码示例:
@contextmanager def managed_two(): a, b = [], {} try: yield a, b finally: a.clear(); b.clear()
如果在yield之后抛出异常,资源会关闭吗?
会,finally块无论如何都会执行,即使在with内部的代码发生错误/异常。
@contextmanager能替代完整的__enter__/__exit__类上下文管理器吗?
在大多数简单情况下 — 可以,但对于更复杂的嵌套状态或继承,使用类会更方便。
手动打开和关闭文件:
f = open('test.txt', 'w') try: f.write('Hello') finally: f.close()
优点:
缺点:
使用@contextmanager临时更改工作目录或打开文件(或环境设置):
@contextmanager def work_in(dirname): import os prev = os.getcwd() os.chdir(dirname) try: yield finally: os.chdir(prev)
优点:
缺点: