其他
利用异常实现越级return(Python实现)
异常是一类消息
所谓异常是消息,指的是一个异常对象可以携带信息,捕获异常(就得到了一个异常对象)或者传递一个异常对象,你就可以从这个异常对象中提取信息。这个消息有时会被认为是一个指令或者信号,即便仅仅只是捕获到了一个特定类型的异常对象,例如,某个无限循环中有一个捕获异常的语句,当捕获到某个类型的异常对象时,它就认为接收到了退出的信号,快速、优雅地退出。
处理异常的语句是流程控制语句
当异常发生时,异常现场所在的局部范围就将被丢弃,并且把相关统计信息存储在一个异常对象中向上冒泡。所谓的局部范围,包括控制语句内部、局部作用域等层次结构中。
如果定义了一个关于异常的类型系统,那么就会配套实现对异常现场的信息搜集策略、异常的冒泡机制和处理异常的语句。
处理异常的语句,专门针对异常消息,会根据条件来判定,对某个异常是放行还是压下。这种流程控制,设计专门用来控制异常对局部范围的破坏。
异常是一种强化版的return
一个函数调用完成时,就会在调用它的地方返回一个值。调用完成,意味着以下3种含义之一:
执行完函数体中的代码,自动返回
遇到return语句,返回
抛出异常
当函数调用完成后,将会销毁调用时所临时创建的局部作用域并回收相关资源。如果函数a在它的函数体内调用了函数b,而b中冒泡了一个异常,未能在a中压下,那么就会在a冒泡给a的调用者。在多层次调用结构中,如果到达顶级层次依然不能被压下,那么程序将会退出。在一些解释器、虚拟机或者某些具有监控功能的执行环境中,程序因为异常退出时,将会自动记录下相关的信息,便于开发者理解问题所在。
抛出异常,未能被压下,将会不断地往上冒泡,并让局部环境不可被再次利用,程序的执行环境将自动回收这个局部环境所占用的资源。不过异常可以引用其它对象,可以被捕获,从中提取信息。正因为可以在退出函数时携带信息冒泡给函数的调用者,所以我说异常是一种强化版的return,调用者完全可以压下异常,从里面取出信息,就像是return给它的一样。
在Python中用异常实现一个越级return机制
import functools
__all__ = ['BubbleReturnSignal', 'leepfrog']
class BubbleReturnSignal(Exception):
'''使用这个异常作为一个return信号,可以实现在多层
调用中,快速退出n个调用层,并存储了一个返回值`r`
:param n: 要跳出的调用层次层数
:param r: 返回值,默认为None
'''
def __init__(self, n: int, r=None):
self.n = n
self.r = r
def test(self):
if self.n <= 0:
return self.r
else:
self.n -= 1
raise self
def leepfrog(fn):
'这个装饰器,使函数自动处理BubbleReturnSignal异常信号,让调用层的退出自动进行'
def wrapper(*args, **kwds):
try:
return fn(*args, **kwds)
except BubbleReturnSignal as exc:
return exc.test()
return functools.update_wrapper(wrapper, fn)
import functools
from leepfrog import leepfrog
def test(fn):
'测试函数,打印一些信息,用于说明'
def wrapper(*args, **kwds):
try:
return fn()
except Exception as exc:
print(f'函数名称: {fn.__qualname__}\t异常参数: {exc.args}\t剩余冒泡: {exc.n}')
raise
return functools.update_wrapper(wrapper, fn)
@test
@leepfrog
def foo1():
return foo2()
@test
@leepfrog
def foo2():
return foo3()
@test
@leepfrog
def foo3():
return foo4()
@test
@leepfrog
def foo4():
return foo5()
@test
@leepfrog
def foo5():
return bar()
@test
def bar():
raise BubbleReturnSignal(4, 'value')
>>> value = foo1()
函数名称: bar 异常参数: (4, 'value') 剩余冒泡: 4
函数名称: foo5 异常参数: (4, 'value') 剩余冒泡: 3
函数名称: foo4 异常参数: (4, 'value') 剩余冒泡: 2
函数名称: foo3 异常参数: (4, 'value') 剩余冒泡: 1
函数名称: foo2 异常参数: (4, 'value') 剩余冒泡: 0
>>> value
'value'
往期推荐