7. python异常处理

mac2022-06-30  24

一、 异常处理机制

异常处理使得程序具有极好的容错性,让程序更加健壮。分离了“业务实现”和“错误处理”代码,提供了更好的程序可读性。异常处理机制主要依赖try,except,else,finally和raise五个关键字。异常处理可以嵌套,但通常不建议超过2层。嵌套过多可能导致程序性能下降,同时降低了可读性。

1. try...except...

#伪代码 try: #业务实现代码 ... except Error type1: #异常处理1 except Error type12: #异常处理2

引发异常过程:若执行try块中业务代码出现了异常,系统自动生成一个异常对象,异常对象会被提交给python解释器。

捕获异常过程:python解释器收到异常对象时,会依次寻找能处理该异常对象的except块(最可能的放最前面)。若找到匹配的,则将该异常对象交给该except块处理,若找不到则报错。

python常见异常类继承关系及异常关系参考

https://docs.python.org/3/library/exceptions.html#exception-hierarchy

inputStr=input("请输入除数:") try: result=20/int(inputStr) print("20除以%s的结果为:%g" % (inputStr,result)) except ValueError: print("输入非数值,请输入一个非0数字") except ArithmeticError: print("输入值为0,请输入一个非0数字") #测试结果 请输入除数:dsads200 输入非数值,请输入一个非0数字 请输入除数:0 输入值为0,请输入一个非0数字 请输入除数:100 20除以100的结果为:0.2

2. 多异常捕获

可以使用一个except块捕获多种类型异常,其实就是构建多个异常类的元组,但这种报错可能过于笼统。

inputStr=input("请输入除数:") try: result=20/int(inputStr) print("20除以%s的结果为:%g" % (inputStr,result)) except (ValueError,ArithmeticError): print("输入了非数值或0,请输入一个非0数字") #测试结果 请输入除数:kkk 输入了非数值或0,请输入一个非0数字 请输入除数:0 输入了非数值或0,请输入一个非0数字 请输入除数:30 20除以30的结果为:0.666667

3. 访问异常信息

python会将异常对象赋值给except块后的异常变量,程序可通过这些变量访问对象信息

inputStr=input("请输入除数:") try: result=20/int(inputStr) print("20除以%s的结果为:%g" % (inputStr,result)) except Exception as e: print("输入了非数值或0,请输入一个非0数字") #异常错误号和详细信息 print(e.args) #输出结果 请输入除数:0 输入了非数值或0,请输入一个非0数字 ('division by zero',)

4. else块

异常处理流程中还可添加else块,当try块中没有出现异常时,程序会执行else块中内容。(可以但没必要,没有异常通常直接放在try后执行就可以了)

inputStr=input("请输入除数:") try: result=20/int(inputStr) print("20除以%s的结果为:%g" % (inputStr,result)) except Exception as e: print("输入了非数值或0,请输入一个非0数字") else: print("未发生异常") #输出结果 请输入除数:100 20除以100的结果为:0.2 未发生异常

5. 使用finally回收资源

除非在程序中使用os._exit(1)强制退出python编译器,否则无论是否发生异常,也无论异常在哪个except块被捕获(包括在try或except中有return语句),finally块总会被执行。finally块必须位于所有的except块后通常情况下,不要在finally块中使用return,raise等导致方法中止的语句,它会让try和except块中的return,raise语句失效(因为无论前面返回什么都会被finally块的return覆盖) import os def test(): fis=None try: fis=open("a.txt") except OSError as e: #返回详细报错信息 print(e.strerror) #测试return返回,仍会执行finally部分 return #删除下面注释,os._exit(1)会直接退出python编译器,不执行finally部分 #os._exit(1) finally: #关闭磁盘文件,回收资源 if fis is not None: try: #关闭资源 fis.close() except OSError as ioe: print(ioe.strerror) print("执行finally块中的资源回收") #测试return,输出 No such file or directory 执行finally块中的资源回收 #测试os._exit(1),输出 No such file or directory

二、 使用raise抛出自定义异常

异常其实是很主观的东西,除了明确会导致程序错误的报错外,也需要结合具体业务确定。例如设置密码时小于6位抛出异常,这就不是python默认的报错,属于自定义异常。

 

1. raise语句

raise语句用于在程序中抛出自定义异常,通常有以下3种用法:

raise # 单独一个raise,抛出当前上下文中捕获的异常(例如except语句中),或默认抛出RuntimeError异常 raise 异常类 # 抛出指定异常类的默认实例 raise 异常对象 # 抛出指定异常对象

自定义异常类

自定义异常类都应该继承Exception类或其子类,定义时通常指定其父类即可。

class MyException(Exception): pass

3种用法最终抛出的都是异常实例,raise语句每次只能抛出一个异常实例,若未在except中捕获,程序会直接报错退出

inputStr=input("请输入除数:") try: num=int(inputStr) #若num>20则抛出自定义异常 if num>20: #引发默认RuntimeError异常 raise result=20/num print("20除以%s的结果为:%g" % (inputStr,result)) except ValueError: print("输入非数值,请输入一个非0数字") except ArithmeticError: print("输入值为0,请输入一个非0数字") except RuntimeError: print("num>20,抛出了自定义RuntimeError异常") #测试输出 请输入除数:21 num>20,抛出了自定义RuntimeError异常

2. except与raise结合使用

实际应用中,当一个异常出现时,可能无法单靠某个方法进行处理,而要由几个方法协作,此时可以通过except与raise结合实现。一个常见的例子是,应用一方面需将报错记入日志文件,另一方面还需返回其他告知用户报错信息。

class MyException(Exception): pass class Bid: #构造方法定义起拍价 def __init__(self,init_price): self.init_price=init_price #定义竞拍函数 def bid_func(self,bid_price): d=0.0 try: d=float(bid_price) except Exception as e: #此处只打印简单异常信息 print("类型转换出现异常",e) #再次抛出自定义异常进行详细处理 raise MyException("竞拍价必须为数值") if self.init_price>d: raise MyException("竞拍价不得低于起拍价") initPrice=d def main(): test=Bid(21.6) try: test.bid_func("yyy") test.bid_func(6) except MyException as e2: #再次捕获异常并进行处理 print("main函数中捕获的异常",e2) main() #测试输出 类型转换出现异常 could not convert string to float: 'yyy' main函数中捕获的异常 竞拍价必须为数值

三、 异常传播轨迹

python提供了traceback模块处理异常传播轨迹,查看异常源头。

traceback模块提供了两个常用方法:

format_exc():将异常传播轨迹转换为字符串traceback.print_exc():将异常传播轨迹输出到控制台或指定文件

print_exc()的完整形式是print_exception(etype,value,tb[,limit[,file]])

etype:指定异常类型value:指定异常值tb:指定异常traceback信息limit:限制显示异常的传播层数,若不设置默认全部显示file:将异常传播轨迹输出到指定文件,若不设置则输出到控制台 import traceback class MyException(Exception): pass def main(): first() def first(): second() def second(): third() def third(): raise MyException("自定义异常信息") try: main() except: #捕获异常并输出至控制台 traceback.print_exc() #捕获异常并输出至指定文件 traceback.print_exc(file=open('log.txt','a')) #输出如下 Traceback (most recent call last): File "E:/pytest02/var.py", line 19, in <module> main() File "E:/pytest02/var.py", line 7, in main first() File "E:/pytest02/var.py", line 10, in first second() File "E:/pytest02/var.py", line 13, in second third() File "E:/pytest02/var.py", line 16, in third raise MyException("自定义异常信息") MyException: 自定义异常信息

输出至文件结果

报错栈应该从下往上看,报错源头是在third方法第16行,对应的正好是抛出异常部分

若不使用该模块,也不捕获异常,python编辑器会利用自带的with_traceback在控制台打出报错栈。

class MyException(Exception): pass def main(): first() def first(): second() def second(): third() def third(): raise MyException("自定义异常信息") main() #控制台报错信息 Traceback (most recent call last): File "E:/pytest02/var.py", line 16, in <module> main() File "E:/pytest02/var.py", line 5, in main first() File "E:/pytest02/var.py", line 8, in first second() File "E:/pytest02/var.py", line 11, in second third() File "E:/pytest02/var.py", line 14, in third raise MyException("自定义异常信息") __main__.MyException: 自定义异常信息

四、 异常处理规则

不要过度使用异常不要使用过于庞大的try块不要忽略捕获到的异常
最新回复(0)