目录
示例1:查看上下文执行的顺序
示例2:动态控制上下文是否抛出异常
示例3:以装饰器的方式为功能函数加装上下文
示例4:过滤异常,不抛出
在之前我们进行过文件操作的学习时,我们为了不忘掉文件操作完毕后关闭文件file.close(),官方推荐推荐我们使用with……as 语句,这其实本质就是运用了python的上下文管理。
而所谓的上下文,其实就是服务运行的状态从进入到退出的一种过程,python中我们常常通过上下文来进行资源的创建与释放。
语法:with……as
本质:
程序执行with中的代码时,会自动先执行enter方法,返回在这个上下文中使用的对象句柄,程序执行完逻辑后自动调用exit来进行资源的释放
示例1:查看上下文执行的顺序
如果上下文中出现异常即执行逻辑代码过程中,上下文是可以捕获异常的,并且默认是抛出异常的
class MyContext:
def __init__(self):
print("in __init__")
def __enter__(self):
print("int __enter__")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
""""""
print("in __exit__")
print("异常的类型exc_type=", exc_type)
print("异常抛出的值exc_type=", exc_val)
print("异常的traceback对象exc_type=", exc_tb)
if __name__ == '__main__':
with MyContext() as t:
print("执行代码逻辑……")
raise Exception("错误解释")
# 执行结果为
"""
in __init__
int __enter__
执行代码逻辑……
in __exit__
异常的类型exc_type= <class 'Exception'>
异常抛出的值exc_type= 错误解释
异常的traceback对象exc_type= <traceback object at 0x000001B52B5465C8>
Traceback (most recent call last):
File "D:/my_all_project/Frame_learning/test.py", line 27, in <module>
raise Exception("错误解释")
Exception: 错误解释
"""
示例2:动态控制上下文是否抛出异常
如果功能函数逻辑中出现异常,而exit方法返回值等价于False就会抛出异常,否则不抛出异常,继续执行上下文外面的业务逻辑
class MyContext:
def __init__(self, flag):
print("in __init__")
self.flag = flag
def __enter__(self):
print("int __enter__")
"可以返回我们定义任何方法或实例化的对象"
return self
def __exit__(self, exc_type, exc_val, exc_tb):
""""""
print("in __exit__")
print("异常的类型exc_type=", exc_type)
print("异常抛出的值exc_type=", exc_val)
print("异常的traceback对象exc_type=", exc_tb)
return self.flag
if __name__ == '__main__':
with MyContext(True) as t:
print("执行代码逻辑……")
raise Exception("错误解释")
print("===>当上下文不抛出异常时,此处可以被打印")
# t 实际就是类Mycontext的实例化对象,其可以调用类中的任意实例方法和属性
示例3:以装饰器的方式为功能函数加装上下文
import contextlib
class MyContext(contextlib.ContextDecorator):
def __init__(self):
print(f'__init__()')
def __enter__(self):
print(f'__enter__()')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print(f'__exit__()')
print(f"exc_type:{exc_type}")
try:
self.write_log()
except Exception as e:
print(e)
@staticmethod
def write_log():
print("开始记录日志")
log = dict(req_body="request", rsp_body="response")
f = open("my_log_test.txt", "w", encoding="utf-8")
import json
f.write(json.dumps(log, ensure_ascii=False))
f.flush()
f.close()
raise Exception("本日志记录报错,不在影响功能函数的正常返回")
@MyContext()
def func(flag):
code, desc = 1, "fail"
try:
if flag:
raise Exception("测试上下文是否能捕获异常")
else:
code, desc = 0, "success"
except Exception as e:
print(f"本初捕获异常:{e},则不会再抛给上下文管理器中")
else:
code, desc = 0, "success"
finally:
return {"code": code, "desc": desc}
if __name__ == '__main__':
ret = func(True)
print(ret)
# 本小例是通过上下文初试为功能函数添加记录日志的功能,不因记录日志出现异常导致功能函数异常,也可以加一个开关,是否记录日志
# 写日志的另一个版本
import threading
class WriteLogContext:
def __init__(self, flag, data):
"""
:param flag: 异常日志是否抛出开关标识
:param data: 日志内容
"""
self.flag = flag
self.data = data
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("异常的类型exc_type=", exc_type)
print("异常抛出的值exc_type=", exc_val)
print("异常的traceback对象exc_type=", exc_tb)
return self.flag
def write_action(self):
"""开启多线程记录日志,发现异常不会抛出外层,但会将异常打印到控制台"""
write_external_thread = threading.Thread(target=self.write_log, args=(self.data,))
write_external_thread.setDaemon(True)
write_external_thread.start()
@staticmethod
def write_log(log):
with open("test.txt", "a") as file:
file.write("入库的操作模拟\n")
file.write(f"{log}\n")
file.flush()
# 模拟异常
raise TypeError("模拟写日志过程中的异常,发现本处报错,并不会影响主功能函数响应")
def access():
# 执行业务
print("business is begin")
# 记录流水
log = "life is short ,i use python ,i use it for make money"
with WriteLogContext(flag=True, data=log) as f:
f.write_action()
# 响应
result = "business is success"
return result
示例4:过滤异常,不抛出
import contextlib
def write_log(data):
if data:
print(111111)
else:
raise Exception("life")
with contextlib.suppress(Exception):
data = dict()
write_log(data)
# suppress类可以过滤指定的异常,不抛出