Python 链式异常(Chained Exception)
链式异常是一种异常处理机制,它允许你在捕获并处理一个异常后,抛出另一个异常,同时清晰地保留原始异常的上下文信息,这意味着当程序出错时,你不仅能知道当前层的错误,还能追溯到根本原因,就像破案时保留完整的证据链一样。
链式异常是一种异常处理机制,它允许你在捕获并处理一个异常后,抛出另一个异常,同时清晰地保留原始异常的上下文信息,这意味着当程序出错时,你不仅能知道当前层的错误,还能追溯到根本原因,就像破案时保留完整的证据链一样。
链式异常的作用:
- 异常溯源,在复杂的代码调用栈中,底层异常可能被上层代码捕获,并转换为一个更符合当前逻辑的异常,链式异常确保了原始异常不丢失,方便调试。
- 异常转译,通过链式异常,在适当的抽象层级上抛出异常(更具业务意义),而底层代码保留具体的异常细节。
一、隐式链式异常
隐式链式异常,如果你在处理一个异常,而你的代码又意外地触发了另一个异常,这就是隐式链式异常。
1.1 隐式链式异常示例
下面的例子中,try模块先引发了IndexError异常,触发except执行,而except执行时又触发了ZeroDivisionError异常,形成一个异常链:
l = [1, 2]
try:
print(l[100])
except:
print(1/0)
当异常未被捕获并打印到控制台时,Python会自动格式化整个异常链,下面的执行结果清晰的显示了2层异常,两层异常中间包含一条回溯消息:During handing of the above exception, another exception occurred.
1.2 __context__属性
隐式链式异常发生时,Python会自动将原始异常设置为新异常的__context__属性,它保留对原始异常对象的引用,以便新异常知道是谁引发了自己:
l = [1, 2]
try:
print(l[100])
except Exception as e:
try:
print(1/0)
except ZeroDivisionError as f:
print("外部异常:",e)
print("内部异常:",f)
print("内部异常的__context__属性:", f.__context__)
通过执行结果可以看到,处理异常e时引发了异常f,f的__context__属性被设置成了e,形成链接关系:
二、显式链式异常
显式链式异常,是指我们使用raise NewException from OriginalException 语法显式地将两个异常关联起来,这会设置NewException(新异常)的__cause__属性,让它指向OriginalException(旧异常),这个技巧可以让多段代码统一行为。
2.1 显式链式异常示例
下面是显式链式异常的一个例子,我们定义一个检查流程,飞机起飞前检查驾驶、副驾驶、乘务员是否就绪,如果未就绪引发统一的PlaneNotReady异常:
class PlaneNotReady(Exception):
pass
def TakeOffCheck(l):
try:
print("驾驶员就绪:", l[0])
print("副驾驶就绪:", l[1])
print("乘务员就绪:", l[2])
except Exception as e:
raise PlaneNotReady("机组人员未就绪") from e # 显式异常链
l = ['Vincent', 'Victor']
TakeOffCheck(l)
这段代码通过raise语句显式的将自定义异常PlaneNotReady连接到人员检查的IndexError异常后:
上面演示了机组人员检查,还可以定义发动机检查、电子检查等各类检查,对于业务人员,通过异常链统一抛出PlaneNotReady方便他们理解,而真正的底层异常,由工程师们通过异常链进行追溯并解决。
2.2 __cause__属性
使用raise语句会显式新异常的设置__cause__属性,用来指向引发自己的异常。
下面我们捕获PlaneNotReady异常,并查看__cause__属性:
class PlaneNotReady(Exception):
pass
def TakeOffCheck(l):
try:
print("驾驶员就绪:", l[0])
print("副驾驶就绪:", l[1])
print("乘务员就绪:", l[2])
except Exception as e:
raise PlaneNotReady("机组人员未就绪") from e
l = ['Vincent', 'Victor']
try:
TakeOffCheck(l)
except PlaneNotReady as f: # 捕获PlaneNotReady异常
print("异常{}由{}引发。".format(f, f.__cause__)) # 查看__cause__属性
三、抑制异常链
如果你不想要异常间的链接信息,可以显式的用raise NewException from None来切断异常间的链接信息,不过这样异常就不具备可追溯性了,通常不建议采用,有兴趣的同学可以自行尝试一下。
更多推荐
所有评论(0)