Context Manager
文档翻译
Python with语句支持上下文管理器定义的运行时上下文概念(runtime context). 用户定义的类实现一对方法来实现此上下文协议, 在语句体执行前进入上下文, 在与语句体结束后退出上下文
-
contextmanager.__enter__()此方法用来实现进入运行时上下文,并返回此上下文关联的对象或自身对象. 此方法返回的值绑定到
as语句后的标识符上.一个返回自身的上下文例子是:
file object.file object返回自身在调用__enter__()方法后.with open("xxx.txt") as f: f.read()一个返回关联对象的例子是:
decimal.localcontext().具体用法省略. -
contextmanager.__exit__(exc_type, exc_val, exc_tb)此方法用来实现退出运行时上下文, 并返回一个布尔值--用来指示如果有应该抑制的异常发生. 如果执行
with语句块时发生了一个异常, 此方法的参数包含异常的类型/值/堆栈信息. 否则, 所有三个参数为None.此方法返回
True时将导致with语句抑制异常的抛出然后继续with语句后的语句.这个语句执行完成后异常将继续向上抛出.此方法执行期间产生的异常将覆盖其他任何with语句体中的异常.传入此方法的异常不应该显式的重新抛出,而是, 这个方法应该返回一个
False来表明此方法成功执行完成并且不会异常抛出的异常. 这用来允许上下文管理器代码容易的来检测是否此方法确实失败.
Python定义了几个上下文管理器来支持线程同步/文件立即关闭等. 除了上下文管理器协议之外, 对特定类型没有特殊的处理.
Python的生成器对象和contextlib.contextmanager装饰器提供了一个简便的方式来实现上下文管理器协议.如果一个generator function被contextlib.contextmanager装饰器装饰, 将返回一个实现了__enter__()和__exit__()方法的上下文管理器, 而不是未被装饰的生成器函数返回的迭代器.
详解
上下文管理器对象存在的目的是管理with语句, 就像迭代器存在是为了管理for语句.
with语句的目的: 简化try/finally模式.
try/finally模式: 用于保证一段代码运行完毕后执行某项操作, 即便那段代码由于异常/return语句或sys.exit()调用而终止, 也会执行指定的操作.
finally子句中的代码通常用于释放重要的资源, 或者还原临时变更的状态.
上下文管理器协议包含__enter__和__exit__方法.
with语句开始运行时, 会在上下文管理器对象上调用__enter__方法.
解释器调用
__enter__方法时, 除了隐式的self之外,不会传入任何参数.
with语句运行结束后, 会在上下文管理器对象上调用__exit__方法, 以此扮演finally子句的角色.
传给
__exit__方法的三个参数解释如下:
exc_type: 异常类(例如ZeroDivisionError)exc_value: 异常实例. 有时会有参数传给异常构造方法, 例如错误消息. 这些参数可以使用exc_value.args获取.traceback:traceback对象
不管控制流程以哪种方式退出with块, 都会在上下文管理器对象上调用__exit__方法, 而不是在__enter__方法返回的对象上调用.
with语句的as子句是可选的. 比如, 打开文件的open函数,必须加上as子句, 以便获得文件的引用.
有些上下文管理器会返回None, 因为没什么有用的对象能提供给用户.
Python标准库示例
- 在
sqlite3模块中用于管理事务 - 在
threading模块中用于维护锁/条件/信号量. - 为
Decimal对象的算术运算设置环境, 参考decimal.localcontext - 为了测试临时给对象打补丁, 参见
unittest.mock.patch函数文档.