上下文管理器——with语句的实现
Posted sfencs-hcy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了上下文管理器——with语句的实现相关的知识,希望对你有一定的参考价值。
前言
with语句的使用给我们带来了很多的便利,最常用的可能就是关闭一个文件,释放一把锁。
既然with语句这么好用,那我也想让我自己写的代码也能够使用with语句,该怎么实现?
下面具体介绍怎样实现一个自己的with语句
使用类实现
要想使用with语句,那就要遵循with语句的使用规矩,也就是上下文管理器协议
这个协议提示我们要在类中去实现两个特殊方法,enter(self)和exit(self, exc_type, exc_val, exc_tb)
有了这两个方法,就可以使用with语句执行了,它的执行过程为先执行enter(self)方法,然后执行你添加在with下的代码,然后最后执行exit方法
如果在执行的过程中出现了异常,那么会自动执行exit方法,
class ErrorProduce:
def __init__(self):
pass
def produce(self):
raise RuntimeError(‘ERROR‘)
class MyWith:
def __enter__(self):
return ErrorProduce()
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type:
print("有错")
#print(exc_val)
#return True
else:
print("完美执行")
with MyWith() as E:
E.produce()
将上述代码运行,会先打印出“有错”,然后再打印出相关错误,然后程序由于错误终止,也就是说,在产生了错误后,还是执行了exit中的代码
如果将exit中的两个注释符号去掉的话,那么再次运行程序不会报错,但会将exc_val中的错误信息打印出来,这里相当于通过exit方法将错误抹除掉了,抹除方式为返回True,但也把with中抛出错误后的所有代码都跳过了。
使用函数实现
通过引入contextlib模块中的contextmanager装饰器,装饰函数使函数可以使用with语句
from contextlib import contextmanager
class ErrorProduce:
def __init__(self):
pass
def produce(self):
raise RuntimeError(‘ERROR‘)
@contextmanager
def my_with():
try:
yield ErrorProduce()
except Exception as e:
print(e)
#raise
finally:
print(‘收尾‘)
with my_with() as E:
E.produce()
print(‘aa‘)
函数中使用了yield,那么它也就变成了生成器,通过yield来划分相当于类中的enter和exit,yield返回的值也就是可以用作as后的值
上段代码中我们发现使用了try,except,finally异常处理的语句,这些语句是必要的吗?
通过测试,如果没有这些异常处理,那么with语句还是可以执行的,这里的意思是可以使用with语句,不会报不能使用with的错误,但是使用后,只有with下的语句中没有抛出异常,那么yield语句后边的代码才会执行,一旦有异常抛出,仍然是程序运行终止。所以,一般还是要配合try,except,finally语句来构造。
还有一点要注意,使用这种方式,异常被except捕捉后,那么这个异常就失效了,一般的我们还是会需要在except中再次将捕捉的异常抛出,也就是在后面加上raise,因为我们使用with只是为了做好一个收尾,如关闭文件句柄。或者另一种方式,我们干脆就不写except语句,直接写finally语句了。
- 文章中出现的棕色加粗的单词,是由于markdown-here将特殊方法两边的下划线转换产生的,原意为特殊方法
以上是关于上下文管理器——with语句的实现的主要内容,如果未能解决你的问题,请参考以下文章