python --上下文管理器实现
前言
with语句的使用给我们带来了,很多的便利,最常用的就是关闭一个文件,释放一把锁。
实现原理
根据with的实现原理“上下文管理器协议”
此协议,提示我们要在类中去实现两个特族方法,enter(self)
和exit(self, exc_type, exc_val, exc_tb)
有了这两个方法,就可以使用with语句了,执行过程:先执行enter()方法, 然后执行添加在with下的代码,
最后执行exit()方法。
如果在执行过程中出现了异常,那么会自动执行exit()方法:
# -*-coding: utf-8-*-
"""
Context Manager:
to implement 'with' in python.
"""
class ErrorProduce(object):
def __init__(self) -> None:
pass
def produce(self):
raise RuntimeError('Error: running time error!!!')
class MyWith(object):
def __enter__(self):
return ErrorProduce()
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type:
print(exc_type)
print("error!")
# print(exc_val) # 放开之后,打印异常的值。
# return True
else:
print("done.")
with MyWith() as E:
E.produce()
结果:
error!
Traceback (most recent call last):
File "d:\workplace\project\machine-project\work\AI-project\context_manager.py", line 29, in
E.produce()
File "d:\workplace\project\machine-project\work\AI-project\context_manager.py", line 12, in produce
raise RuntimeError('Error: running time error!!!')
RuntimeError: Error: running time error!!!
由结果可知,会先打印‘error!’,然后打印相关错误,再程序由于错误终止,即产生了错误之后,还是执行了exit中的代码。
如果将exit中的注释放开,再次运行,程序将不会报错,但是会将exc_val中的错误信息打印出来,相当于exit方法将错误抹除了
抹除方式返回True,但是也将with中抛出错误后的代码都跳过了。
使用函数实现
引入contextlib模块中的contextmanage装饰器, 装饰器使函数可以使用with语句。
from contextlib import contextmanager
class ErrorProduce:
def __init__(self) -> None:
pass
def produce(self):
raise RuntimeError("ERROR")
@contextmanager
def my_with():
try:
yield ErrorProduce()
except Exception as e:
print(e)
finally:
print("收尾")
with my_with() as E:
E.produce()
print('aa')
结果:
ERROR
收尾
函数中使用了yield, 为一个生成器, 通过yield 来划分相当于类中的enter和exit, yield返回值可以当作as后的值
上段代码中我们发现使用了try,except,finally异常处理的语句,这些语句是必要的吗?
通过测试,如果没有这些异常处理,那么with语句还是可以执行的,这里的意思是可以使用with语句,
不会报不能使用with的错误,但是使用后,只有with下的语句中没有抛出异常,那么yield语句后边的代码才会执行,
一旦有异常抛出,仍然是程序运行终止。所以,一般还是要配合try,except,finally语句来构造。
还有一点要注意,使用这种方式,异常被except捕捉后,那么这个异常就失效了,
一般的我们还是会需要在except中再次将捕捉的异常抛出,也就是在后面加上raise,
因为我们使用with只是为了做好一个收尾,如关闭文件句柄。
或者另一种方式,我们干脆就不写except语句,直接写finally语句了。