Python上下文管理器(Context managers)
上下文管理器(context manager https://docs.python.org/zh-cn/3/glossary.html#term-context-manager )
在 with 语句中使用,通过定义 __enter__() 和 __exit__() 方法来控制环境状态的对象。
【with 语句上下文管理器https://docs.python.org/zh-cn/3/reference/datamodel.html#context-managers
with 语句 https://docs.python.org/zh-cn/3/reference/compound_stmts.html#with】
上下文管理器允许你在有需要的时候,精确地分配和释放资源。上下文管理器是指在一段代码执行之前执行一段代码,用于一些预处理工作;执行之后再执行一段代码,用于一些清理工作。比如打开文件进行读写,读写完之后需要将文件关闭。在上下文管理协议中,有两个方法__enter__和__exit__,分别实现上述两个功能。比如在数据库操作中,操作之前需要连接数据库,操作之后需要关闭数据库。
讲到上下文管理器最广泛的案例就是with语句了。with语法。基本语法格式为:
with EXPR [as VAR]:
BLOCK
其中的EXPR是上下文表达式(Context Expression),该表达式要返回一个上下文管理器对象。返回的是一个对象,var用来保存EXPR表达式返回的对象,可以有单个或者多个返回值。这里的as VAR可以省略,如果指定了 as 子句的话,会将上下文管理器的 __enter__() 方法的返回值赋值给VAR。VAR可以是单个变量,或者由“()”括起来的元组(不能是仅仅由“,”分隔的变量列表,必须加“()”)。
代码块BLOCK, with 语句包裹起来的代码块,在执行语句体之前会调用上下文管理器的 __enter__() 方法,执行完语句体之后会执行 __exit__() 方法。在这里VAR可以当做普通变量使用。
以打开一个文件hello.txt并写入 hello, world 为例
我们先对比如下两种方式:
方式一,代码如下:
f = open("d:/hello.txt", 'w')
try:
f.write('hello, world')
finally:
f.close()
方式二,代码如下:
with语句通过在上下文管理器中封装try…finally语句的标准用法来简化异常处理
with open('d:/hello.txt', 'w') as f:
f.write('hello, world')
Python 对一些内建对象进行改进,加入了对上下文管理器的支持,可以用于 with 语句中,比如可以自动关闭文件、线程锁的自动获取和释放等。方式二可以验证,内置函数 open ()就具有对上下文管理器的支持。
如何来实现我们自己的上下文管理器
下面介绍如何来实现我们自己的上下文管理器?或者说,如何使自定义的对象支持with?
自定义对象实现上下文管理器即可。有两种方式:基于类实现方式和基于生成器实现方式。
上下文管理器是一个简单的协议(接口),自定义的对象需要遵循这个协议(接口)来支持with语句。具体做法,向自定义对象中添加__enter__和__exit__方法,Python将在资源管理的适当时间调用这两种方法。
还是用前面提到的 打开一个文件hello.txt并写入 hello, world 例子,
☆基于类实现方式
#先定义类
class ManagedFile:
def __init__(self, name):
self.name = name
def __enter__(self):
self.file = open(self.name, 'w')
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
#提示ManagedFile类遵循上下文管理器协议,与原open()一样也支持with语句
with ManagedFile('d:/hello.txt') as f:
f.write('hello, world')
进入with语句上下文,Python调用__enter__获取资源,离开with语句上下文,Python调用__exit__释放资源。
☆基于生成器实现
利用标准库contextlib模块的contextmanager装饰器重写之前的ManagedFile上下文管理器
from contextlib import contextmanager
@contextmanager
def managed_file(name):
try:
f = open(name, 'w')
yield f
finally:
f.close()
#提示managed_file遵循上下文管理器协议
with managed_file('d:/hello.txt') as f:
f.write('hello, world')
python3,浅谈with的神奇魔法 https://blog.csdn.net/lxy210781/article/details/81176687
深入理解 Python 中的上下文管理器 https://juejin.cn/post/6844903795403522056
python之上下文管理器——with语句详解https://blog.csdn.net/weixin_45950544/article/details/103940387
python上下文管理器ContextLib及with语句https://blog.csdn.net/pipisorry/article/details/50444736