python 标准库 上下文管理类型
每日一词:
intresting :
US [‘ɪntrəstɪŋ] UK [‘ɪntrəstɪŋ]
- adj.有趣的;有吸引力的
- v.“interest”的现在分词
- Web有意思的;令人感兴趣的;引人入胜的
比较级:more interesting
最高级:most interesting
大部分时候,你都是一个人在默默努力,这样,距离成功才会更进一步。
– 凭海临风语录
python 标准库学习 上下文管理
什么是上下文管理器?上下文管理器就是一个用装饰器实现上下文协议管理的对象。主要用于保存和恢复各种全局状态,例如关闭文件等。下面我们来了解具体的内容。
函数
contextmanager.__enter__()
进入运行时上下文并返回此对象或关联到该运行时上下文的其他对象。 此方法的返回值会绑定到使用此上下文管理器的
with
语句的as
子句中的标识符。一个返回其自身的上下文管理器的例子是 file object。 文件对象会从 enter() 返回其自身,以允许open()
被用作with
语句中的上下文表达式。一个返回关联对象的上下文管理器的例子是decimal.localcontext()
所返回的对象。 此种管理器会将活动的 decimal 上下文设为原始 decimal 上下文的一个副本并返回该副本。 这允许对with
语句的语句体中的当前 decimal 上下文进行更改,而不会影响with
语句以外的代码。contextmanager.__exit__(*exc_type*, *exc_val*, *exc_tb*)
退出运行时上下文并返回一个布尔值旗标来表明所发生的任何异常是否应当被屏蔽。 如果在执行
with
语句的语句体期间发生了异常,则参数会包含异常的类型、值以及回溯信息。 在其他情况下三个参数均为None
。自此方法返回一个真值将导致with
语句屏蔽异常并继续执行紧随在with
语句之后的语句。 否则异常将在此方法结束执行后继续传播。 在此方法执行期间发生的异常将会取代with
语句的语句体中发生的任何异常。传入的异常绝对不应当被显式地重新引发 —— 相反地,此方法应当返回一个假值以表明方法已成功完成并且不希望屏蔽被引发的异常。 这允许上下文管理代码方便地检测__exit__()
方法是否确实已失败。
一个文件操作实例
1 | with open("/etc/hosts", "r") as file: |
此时的open返回的对象file,就实现了管理打开文件、关闭文件的上下文管理协议。
with 语句上下文管理器
上下文管理器 是一个对象,它定义了在执行 with
语句时要建立的运行时上下文。 上下文管理器处理进入和退出所需运行时上下文以执行代码块。 通常使用 with
语句(在 with 语句 中描述),但是也可以通过直接调用它们的方法来使用。
上下文管理器的典型用法包括保存和恢复各种全局状态,锁定和解锁资源,关闭打开的文件等等。
要了解上下文管理器的更多信息,请参阅 上下文管理器类型。
object.__enter__
(self)进入与此对象相关的运行时上下文。
with
语句将会绑定这个方法的返回值到as
子句中指定的目标,如果有的话。object.__exit__
(self, exc_type, exc_value, traceback)退出关联到此对象的运行时上下文。 各个参数描述了导致上下文退出的异常。 如果上下文是无异常地退出的,三个参数都将为
None
。如果提供了异常,并且希望方法屏蔽此异常(即避免其被传播),则应当返回真值。 否则的话,异常将在退出此方法时按正常流程处理。请注意__exit__()
方法不应该重新引发被传入的异常,这是调用者的责任。
可以参考
自定义上下文管理
实现__enter__
和 __exit__
方法就是一个实现了上下文管理的类。
1 | class ContextManager(object): |
另一个不返回当前类的上下文管理器的例子:
1 | class InnerContext(object): |
异常处理的例子 :
1 | class ContextManager(object): |
contextlib 模块
这个内置模块实现了上下文管理,使用with
关键字。
主要方法如下(节选自源码):
1 | __all__ = ["asynccontextmanager", "contextmanager", "closing", "nullcontext", |
核心类
class contextlib.AbstractContextManager
同步的上下文管理类
class contextlib.AbstractAsyncContextManager
异步的上下文管理类
装饰器
-
一个实现了上下文资源管理的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15from contextlib import contextmanager
def managed_resource(*args, **kwds):
# Code to acquire resource, e.g.:
resource = acquire_resource(*args, **kwds)
try:
yield resource
finally:
# Code to release resource, e.g.:
release_resource(resource)
with managed_resource(timeout=3600) as resource:
# Resource is released at the end of this block,
# even if code in the block raises an exception==tips== : 注意这里 返回的是generator对象,每次迭代器只会yield一个对象出来,这个值会用在
with
语句中,绑定到as
后的对象上。 @contextlib.asynccontextmanager`
下面是一个实现了异步上下文管理器的实例,关于操作数据库对象
1
2
3
4
5
6
7
8
9
10
11
12
13from contextlib import asynccontextmanager
async def get_connection():
conn = await acquire_db_connection()
try:
yield conn
finally:
await release_db_connection(conn)
async def get_all_users():
async with get_connection() as conn:
return conn.query('SELECT ...')
其他方法
contextlib.closing
(thing)返回一个上下文管理对象,在语句结束之前被调用
相当于下面的实现
1
2
3
4
5
6
7
8from contextlib import contextmanager
def closing(thing):
try:
yield thing
finally:
thing.close()也可以这样实现
1
2
3
4
5
6from contextlib import closing
from urllib.request import urlopen
with closing(urlopen('http://www.python.org')) as page:
for line in page:
print(line)
contextlib.nullcontext
返回一个上下文管理对象( 实现了
__enter__
方法)一个例子
1
2
3
4
5
6
7
8
9def myfunction(arg, ignore_exceptions=False):
if ignore_exceptions:
# Use suppress to ignore all exceptions.
cm = contextlib.suppress(Exception)
else:
# Do not ignore any exceptions, cm has no effect.
cm = contextlib.nullcontext()
with cm:
# Do something
另一个例子
1 | def process_file(file_or_path): |
contextlib.suppress
返回一个声明的异常对象的上下文管理
一个例子:
1
2
3
4
5
6
7from contextlib import suppress
with suppress(FileNotFoundError):
os.remove('somefile.tmp')
with suppress(FileNotFoundError):
os.remove('someotherfile.tmp')和下面的代码等价
1
2
3
4
5
6
7
8
9try:
os.remove('somefile.tmp')
except FileNotFoundError:
pass
try:
os.remove('someotherfile.tmp')
except FileNotFoundError:
pass
contextlib.redirect_stdout
临时输出标准输出的上下文管理器
contextlib.redirect_stderr
临时输出标准错误的上下文管理器
class
contextlib.ContextDecorator
允许一个类像装饰器那样使用,
ContextDecorator
正好实现了__enter__
and__exit__
方法。使用contextlib就自动调用这个装饰器。
一个实例
1 | from contextlib import ContextDecorator |
其实本质上就是实现了语法糖
例如:
1 | def f(): |
ContextDecorator
允许你这样使用:
1 |
|
允许你通过继承ContextBaseClass和ContextDecorator,实现Mixin class(我也不知道该如何翻译,姑且翻译成混合继承吧)
1 | from contextlib import ContextDecorator |
- class
contextlib.ExitStack
一个上下文管理器可以被设计成自动合并其他上下文管理器,清除方法(栈),尤其是那些需要输入数据的功能实现。
这里我看了源码,通过一个栈结构管理上下文管理, 其实就是实现了一个上下文管理器栈
下面是一个例子:
1 | with ExitStack() as stack: |
个人理解这是一个低级的api,内部实现了,你无需关心何时该调用该方法,由python内部去处理。
小结
最近恰好看flask的源码,flask的生命周期管理也是使用上下文管理装饰器实现。
等有空再更新一篇吧。
今天就到这里,祝大家周末愉快!