编写软件,大部分代码都是在处理各种异常和错误。我们常常会遇到这样的场景,代码流程需要一层层的判断底层的返回是否成功,这样的代码写起来其实很费劲,为了一个可能出现的错误,要在每一个获取返回值的地方写if判断。其实,这个时候,使用raise来抛出一个异常,比用return返回标志位(True或False),更加简单,代码的可读性和可维护性也更好,代码的层次感也越强。
return语句只能返回到上一层调用的地方,如果调用层次比较多,底层的问题,要层层传递上来就太费劲了,这样代码写起来看起来都很别扭。return主要还是用来返回数据的,而raise是更好的“返回异常”的方式。
在一个处于层层调用关系的流程中,不管哪个地方raise抛出一个异常,我们只需要在流程需要的地方try...except...捕获异常,就可以了。raise抛出异常后,代码返回到最近的try...except...的地方(这是个与return很不一样的细节),这样中间流程的代码,写起来就会很轻松惬意优雅。而且,如果中间虽捕获了异常,但是不对异常进行处理,也可以直接独立的依据raise,再次将异常抛出,交给更上层来处理。
举个例子:
>>> def level_1():
... raise ValueError('this is a value error')
...
>>> def level_2():
... level_1()
... print('in level 2')
...
>>> def level_3():
... level_2()
... print('in level 3')
...
>>> def top():
... level_3()
... print('in top')
...
>>> try:
... top()
... except:
... print('catch exception from level 1')
...
catch exception from level 1
>>>
以上示例代码,在最底层的函数raise一个ValueError异常,top函数与直接raise异常的函数,中间还经过了两层调用。不过,运行程序发现,最底层raise之后,在最顶层直接捕获异常,而且,很重要的细节是,代码中所有的打印都没有执行,代码相当于从最底层直接return到了最顶层try...except...的地方。
这种代码的写法,比一层层return再判断,要简单很多。这种层层调用在软件中很常见,稍微封装一下底层接口代码,层次关系就出现了。如果再学会了自定义Python的异常类,配合这种写法,您的代码一定会更加漂亮性感!
单独一句raise的作用
代码中常常能看到单独使用一句raise,后面不带任何参数,这样写的作用是,将向下文当前的异常抛出(raise语句不带参数的默认动作)。
def do_raise():
raise ValueError('test value error')
def middle():
try:
do_raise()
except:
print('something wrong')
raise
def top():
try:
middle()
except ValueError as e:
print(repr(e))
top()
在middle函数中,单独使用raise语句,它将会被do_raise抛出的异常,直接在此抛出。middle函数不对此异常进行处理,而是交给上层代码去处理。这段代码的运行效果如下:
$ python3 raise.py
something wrong
ValueError('test value error')
raise在层层调用的代码流程中,简化了异常处理的代码编写,并形成了自己独有的异常处理层次关系,使得代码在处理异常时非常灵活高效。