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
函数文档.