Python上下文管理器(Context managers)

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()

【若d盘中已有hello.txt文件将被替换掉,没有则新建hello.txt文件,双击它,可以看到其中的内容】

方式二,代码如下:

with语句通过在上下文管理器中封装try…finally语句的标准用法来简化异常处理。

with open('d:/hello.txt', 'w') as f:
    f.write('hello, world')

两种方式代码是等价的,方式二使⽤了with,会自动关闭已打开的⽂件,如果在往⽂件写数据时发⽣异常,它也会尝试去关闭⽂件。

【Python 对一些内建对象进行改进,加入了对上下文管理器的支持,可以用于 with 语句中,比如可以自动关闭文件、资源的加锁和解锁等。】

 

如何来实现我们自己的上下文管理器

下面介绍如何来实现我们自己的上下文管理器?或者说,如何使自定义的对象支持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')

这个⽅法使用了⽣成器(使用了yield)和装饰器(使用了@函数名)的⼀些知识。
 

 

附录

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

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学习&实践爱好者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值