十八. 上下文管理协议 __enter__ 和 __exit__
十八. 上下文管理协议 __enter__ 和 __exit__
1.什么是上下文管理协议
上下文管理协议就是 with 语句, 为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__()
和__exit__()
方法
- with 对象,触发对象的
__enter__
的执行 - 在with同一级别写代码, 脱离了with,就会执行
__exit__
class Open:
def __enter__(self):
print("----->enter执行了")
return "enter的返回值"
def __exit__(self, exc_type, exc_val, exc_tb):
print("----->exit执行了")
with Open() as f: # 触发 __enter__
print(f)
print("---->结束") # 触发 __exit__
'''
----->enter执行了
enter的返回值
----->exit执行了
----->结束
'''
2.exit 的三个参数
- exc_type :异常类型
- exc_val :异常信息
- exc_tb :追溯信息
with语句中代码块出现异常,则with后的代码都无法执行
class Open:
def __enter__(self):
print("----->enter执行了")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("异常类型--->",exc_type)
print("异常信息--->",exc_val)
print("追溯信息--->",exc_tb)
print("----->exit执行了")
with Open() as f:
print(f)
raise AttributeError("这里设置一个异常,则下面代码不会再运行")
print("---->结束") # 这一行无法运行
'''
----->enter执行了
<__main__.Open object at 0x0000022E31429D08>
异常类型---> <class 'AttributeError'>
异常信息---> 这里设置一个异常,则下面代码不会再运行
追溯信息---> <traceback object at 0x0000022E3142F0C8>
----->exit执行了
(抛出异常) : AttributeError: 这里设置一个异常,则下面代码不会再运行
'''
3.为什么要使用上下文管理器
- 可以自动的操作(创建/获取/释放)资源,如文件操作、数据库连接
- 无需在使用
try...execept...
去处理异常
如果__exit__()
返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行
class Open:
def __enter__(self):
print("----->enter执行了")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("异常类型--->",exc_type)
print("异常信息--->",exc_val)
print("追溯信息--->",exc_tb)
print("----->exit执行了")
return True
with Open() as f:
print(f)
raise AttributeError("这里设置一个异常,但上面exit的return是True, 所以该异常被忽略")
print("---->结束") # 这一行正常运行
'''
----->enter执行了
<__main__.Open object at 0x000001D6CC399D08>
异常类型---> <class 'AttributeError'>
异常信息---> 这里设置一个异常,则下面代码不会再运行
追溯信息---> <traceback object at 0x000001D6CC39F0C8>
----->exit执行了
---->结束
Process finished with exit code 0
'''
4.自定义一个 open ,可以进行文件操作
class Open:
def __init__(self,path,mode="rt",encoding="utf-8"):
# 拿到一个open对象,那么我们就可以借用open对象的方法,当然你也可以重写方法read(),write()等
self.f = open(path,mode,encoding=encoding)
def __enter__(self):
return self.f # 返回open对象 (作用就是为了可以使用它的方法)
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type: # 如果不为空,说明with上下文内部出错误了
print(exc_val) # 打印一下错误信息
print("错误已被忽略") # 提示一下不会结束程序
return True # return True 就是为了忽略错误
self.f.close() # 当with内部没有错误, 正常结束时进行资源清理工作(关闭文件等)
with Open("test.py","rt",encoding="utf-8") as f:
res = f.read()
print(res)
5.总结
使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在exit中定制自动释放资源的机制,你无须再去关注这个问题