Python 中高级知识 contextlib
contextlib 模块
contextlib 模块提供了3个对象:装饰器contextmanager、上下文管理器closing。使用这些对象,可以对已有的生成器函数或者对象进行包装,加入对上下文管理协议的支持,避免了专门编写上下文管理器来支持with语句。同时我们也是要函数ExitStack/nested做些思维发散
contextlib源码片段
__all__ = ["asynccontextmanager", "contextmanager", "closing", "nullcontext", "AbstractContextManager", "AbstractAsyncContextManager", "AsyncExitStack", "ContextDecorator", "ExitStack", "redirect_stdout", "redirect_stderr", "suppress"] |
装饰器 contextmanager
contextmanager用于对生成器函数进行装饰,生成器函数被装饰以后,返回的是一个上下文管理器,其 __enter__()和__exit__()方法由contextmanager负责提供,而不再是之前的迭代子。被装饰的生成器函数只能产生一个值,否则会导致异常RuntimeError;产生的值会赋值给as子句中的target,如果使用了as子句的话。
下面看一个简单的例子:
# !/usr/bin/env python3# -*- coding: utf-8 -*-from contextlib import contextmanager@contextmanagerdef demo(): print('[Allocate resources]') print('Code before yield-statement executes in __enter__') yield '*** contextmanager demo ***' print('Code after yield-statement executes in __exit__') print('[Free resources]')with demo() as value: print('Assigned Value: %s' % value) |
[Allocate resources]Code before yield-statement executes in __enter__Assigned Value: *** contextmanager demo ***Code after yield-statement executes in __exit__[Free resources] |
可以看到,生成器函数中 yield 之前的语句在__enter__()方法中执行,yield 之后的语句在__exit__()中执行,而 yield 产生的值赋给了 as 子句中的 value 变量。
需要注意的是,contextmanager只是省略了__enter__()/__exit__()的编写,但并不负责实现资源的“获取”和“清理”工作;“获取”操作需要定义在yield语句之前,“清理”操作需要定义 yield 语句之后,这样with语句在执行__enter__()/__exit__()方法时会执行这些语句以获取/释放资源,即生成器函数中需要实现必要的逻辑控制,包括资源访问出现错误时抛出适当的异常。
函数 ExitStack/nested
错误修正 link
python3.3 以后: ExitStack
python3.3以前: nested 可以将多个上下文管理器组织在一起,避免使用嵌套 with 语句。
nested 语法
with nested(A(), B(), C()) as (X, Y, Z): # with-body code here |
类似于:
nested 执行过程
with A() as X: with B() as Y: with C() as Z: # with-body code here |
需要注意的是,发生异常后,如果某个上下文管理器的__exit__()方法对异常处理返回 False,则更外层的上下文管理器不会监测到异常.
上下文管理器 closing
closing 的实现如下:
上下文管理 closing 实现
class closing(object): # help doc here def __init__(self, thing): self.thing = thing def __enter__(self): return self.thing def __exit__(self, *exc_info): self.thing.close() |
上下文管理器会将包装的对象赋值给as子句的target变量,同时保证打开的对象在with-body 执行完后会关闭掉。closing上下文管理器包装起来的对象必须提供close()方法的定义,否则执行时会报 AttributeError 错误。
自定义支持 closing 的对象
class ClosingDemo(object): def __init__(self): self.acquire() def acquire(self): print 'Acquire resources.' def free(self): print 'Clean up any resources acquired.' def close(self): self.free() with closing(ClosingDemo()): print 'Using resources' |
结果输出如下:
自定义 closing 对象的输出结果
Acquire resources.Using resourcesClean up any resources acquired. |
closing 适用于提供了 close()实现的对象,比如网络连接、数据库连接等,也可以在自定义类时通过接口 close() 来执行所需要的资源“清理”工作。

浙公网安备 33010602011771号