一、说明
关于异常捕获try-except:在学java的时候就被教育异常捕获也是java相对c的一大优点,几年下来多少也写了些代码,但异常捕获总只得其形未得其神,在自己这只是让发生错误的程序在不必要终止时不终止而已。
关于主动抛出异常raise:前段时间看到robot framework判断测试用例运行失败,是监控自己使用raise主动抛出的异常,这才有了主动抛出异常的概念。
关于断言assert:前段时间写了个模糊测试工具,然后发现很多可以导致宕机的问题,开发排查后说是新加的断言导致的问题;当时对断言并不太清楚,他们修复问题后也没深入追究。
其实这里最主要是说,了解之后发现,try-except、raise和assert其实有相当大的关系,有必要记一记。
二、异常捕获try-except
异常捕获没有很多说的,各语言意思都差不多只是书写格式有点区别,我们直接上示例:
deftestTryExcept():try:
file_obj= open('myfile.txt')
str_var=file_obj.readline()
int_var=int(str_var.strip())#如果检测到是OSError类异常,进行以下处理
#OSError as err表示给当前捕获到的OSError异常起别名为err;名字叫什么可以是随意的
exceptOSError as err:print(f"OS error: {err}")#如果不是OSError检测到是ValueError,进行以下处理
exceptValueError:print("Could not convert data to an integer.")#如果既不是OSError也不是ValueError而是其他异常,进行以下处理
#Exception as e表示给当前捕获到的异常起别名为e;名字叫什么可以是随意的
#如果不需要打印e,那么Exception as e这部分可省略
#我自己而言,不会捕获具体的异常类型,即不会像上面一样单独捕获OSError和ValueError,就只写下边这么一个except就完了
exceptException as e:print(f"Unexpected error: {e}")#前边的print(f"{e}")形式只会打印异常的概要信息,如果要打印异常的堆栈信息要使用别的写法
#方法一:traceback
#import traceback
#print(f"{traceback.format_exc()}")
#方法二:logging.exception()
#import logging
#logging.exception(e)
#不管是否发生异常,finally部分都会执行
#对于异常捕获而言,finally部分经常可以没有,至少一直以来我都不怎么写
finally:
file_obj.close()
三、主动抛出异常raise
在上面的try-except中我们都是被动等待异常出现然后进行捕获----事实上这些被动等待的异常本质上也是库函数使用raise主动抛出的----我们完全可以使用raise主动抛出异常,进一步说我们可以使用raise抛出自己定义的异常。
主动抛出异常的好处,一是可以抛出在语法上不被认为是异常但在功能上我们认为是异常的情况(如用户名密码错误等),二是可以自定义自己的异常报错语句更方便异常的定位和排查。
注意,主动抛出的异常扔是异常,所以仍可以用try-except来捕获。
#自定义的异常类都要继承Exception类,至少是间接继承Exception类
classPasswordException(Exception):#在init方法中定义一个password变量
def __init__(self,password):
self.password=passworddef __str__(self):returnrepr(self.password)deftestRaise():#主动抛出异常示例
try:
username= input("please enter your username:")#输入的用户名不是admin就抛出异常Exception
if username != "admin":raise Exception(f"maybe your privilege is not enough: {username}")#不过要注意这样raise一个Exception会丢掉异常的堆栈信息,如果要原样返回异常可以直接写raise
#raise
#可以看到打印的是我们自定义的异常语句
exceptException as e:print(f"{e}")#主动抛出自定义异常示例
try:
password= input("please enter your password:")#输入的密码不是123456就抛出自定的的PasswordException异常
if password != "123456":raisePasswordException(password)#我们自定义的异常有password变量,所以我们可以直接选择把变量打印出来
exceptPasswordException as e:print(f"PasswordException: {e.password}")if __name__ == "__main__":
testRaise()
四、断言assert
4.1 assert本质讨论
assert使用形式如下:
assert expression ["," expression]
如果只接一个表达示,那相当于如下:
if __debug__:if not expression: raise AssertionError
如果接两个表达示,那相当于如下:
if __debug__:if not expression1: raise AssertionError(expression2)
其中涉及的__debug__和AssertionError如下:
__debug__:如果程序运行时不带-O参数,则为True;反之则为False。
AssertionError:就是一个继承Exception类的异常类,其源代码在builtins.py中,如下图所示
所以,本质上,assert就是raise的一个宏定义;当前紧接的好个表达示不为True时,就抛出异常。
4.2 assert的使用
assert经常用于参数被使用前的检查操作,如果检查未通过则直接抛出异常及早发现错误,避免明显错误的参数还被往后传递。
注意,由于assert本质上还是raise,所以一样可以使用try-except捕获,而不是说断言错误程序就一定会终止。
deftestAssert():try:
int_var= int(input("please enter a positive number:"))#如果输入的数值不大于0,断言失败,抛出异常
assert int_var >0except:print(f"sorry, please enter a positive number")print(f"what you enter is: {int_var}")if __name__ == "__main__":
testAssert()
参考: