接上篇文章:
python中的异常处理(小白必看的史上最全异常处理总结!)【上篇】
raise
当程序出现错误时,系统会自动引发异常。除此之外,python也允许程序自动引发异常,自行引发异常使用raise来完成
引发异常
其实异常是一种主观的说法,对我们来说是一件不好的事,但对另一方面来说可能并不是那么糟糕。就拿实际中的例子来说,近来北京天天下雨,就好比我周六约了朋友去打篮球,结果周六那天突降大雨,篮球我是打不了了,这就打破了我原定的计划,对于我来说这就是一种异常,但是另一方面,转眼干旱地区和农作物现在正需要点雨水的滋润,那对于他们来说这就是正常的。
所以很多时候,系统是否要引发异常,可能需要根据应用的业务需求来定,如程序中的数据、执行与既定的业务需求不符,这就是一种异常。由于与业务需求不符而产生的异常必须由程序员来决定引发,系统是无法引发这种异常的。
如果想要在程序中自行引发异常,就需要用到raise语句了
raise语句有的三种常用的方法:
1、raise:这是单独的一个raise。该语句引发当前上下文中捕获的异常(如在except块中),或者默认引发RuntimeError异常
2、raise 异常类:raise后带一个异常类。该语句引发指定异常类的默认实例
3、raise 异常对象:引发指定的异常对象
这三种最终都要引发一个异常实例(即指定的是异常类,实际上也是引发该类的默认实例),raise语句每次只能引发一个异常实例。
raise的作用:显式的抛出异常。当出现异常时,raise后面的语句就不会执行
def test(count):
if count == 100:
raise ValueError("I am error")
print(count)
test(100)
运行结果:
Traceback (most recent call last):
File "D:/PycharmProjects/wangyongha/爬虫/pachong.py", line 718, in <module>
test(100)
File "D:/PycharmProjects/wangyongha/爬虫/pachong.py", line 715, in printCount
raise ValueError("I am error")
ValueError: I am error
※如果test()传参不等于100,那么就直接执行print(count)语句
raise后面可以是一个class、也可以是一个对象
def test(count):
if count == 100:
raise ValueError("I am error") #这是实例对象
#raise ValueError #这是class
print(count)
try:
test(100)
except ValueError as e: #e为ValueError对象
print(e)
或者
# try:
# test(100)
# except ValueError:
# print('我捕获到一个valueError错误')
运行结果:
I am error
当python解释器接收到开发者自行引发异常时,同样会终止当前的执行流,跳到异常对应的except块,由except块来处理异常。也就是说不管是系统自动引发的异常还是我们自己手动引发的异常,python解释器对异常的处理其实没有任何差别。
自定义异常类
很多时候,程序可选择引发自定义异常,因为异常的类名通常也包含了该异常的有用信息。所以在引发异常时,应选择合适的异常类,从而可以明确的描述该异常情况。
用户自定义异常类都应该继承Eexception基类或者Exception的子类,在自定义异常类时基本不需要写更多的代码,只需要指定自定义异常类的父类就可以了
例:
class AuctionException(Exception):
pass
自定义异常类需要实现__str__方法来定义需要抛出的异常信息
raise语句主动抛出异常。
捕获异常需要通过try…except 语句,其中"except"语句必须有用相同的异常(或者直接抛出Exception)来抛出类对象
#1.用户自定义异常类型
class TooLongExceptin(Exception):
def __init__(self,Leng):
self.leng = Leng
def __str__(self):
print("姓名长度是"+str(self.leng)+",超过长度了")
#!/usr/bin/python
# -*- coding: UTF-8 -*-
def name_Test():
try:
name = input("enter your naem:")
if len(name)>4:
raise TooLongExceptin(len(name))
else :
print(name)
except TooLongExceptin as e_result: #e_result是基于TooLongExceptin类创建的实例 print("捕捉到异常了")
print("打印异常信息:",e_result)
#调用函数,执行
name_Test()
运行结果:
C:\Users\admin\AppData\Local\Programs\Python\Python39\python.exe C:/Users/admin/Desktop/test.py
enter your naem:223rfsv
Traceback (most recent call last):
File "C:\Users\admin\Desktop\test.py", line 7, in name_Test
raise TooLongExceptin(len(name))
NameError: name 'TooLongExceptin' is not defined
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\admin\Desktop\test.py", line 15, in <module>
name_Test()
File "C:\Users\admin\Desktop\test.py", line 11, in name_Test
except TooLongExceptin as e_result: #e_result是基于TooLongExceptin类创建的实例 print("捕捉到异常了")
NameError: name 'TooLongExceptin' is not defined
Process finished with exit code 1
except和raise同时使用
在实际应用中对异常可能需要更复杂的处理方式,当一个异常出现时,单靠某个方法无法完全处理该异常,必须由几个方法协作才可以完成处理该异常,也就是说在异常出现的当前方法中,程序只对异常进行部分处理,还有一些处理需要在该方法的调用者中才能完成,所以应该再次引发异常,让该方法的调用者也能捕获到异常。
这个可以通过在except块中结合raise语句来完成
例:
class AuctionException(Exception): pass
class AuctionTest:
def __init__(self,init_price):
self.init_price = init_price
def bid(self,bid_price):
d = 0.0
try:
d = float(bid_price)
except Exception as e:
print("转换出异常:",e)
#再次引发自定义异常
raise AuctionException("竞拍价必须是数值,不能包含其他字符!")
if self.init_price > d:
raise AuctionException("竞拍价比起拍价低,不允许竞拍!")
init_price = d
def main():
at = AuctionTest(20.4)
try:
at.bid("df")
except AuctionException as ae:
#再次捕获到bid()方法中的异常,并对该异常进行处理
print('main函数捕获的异常:',ae)
main()
运行结果:
转换出异常: could not convert string to float: 'df'
main函数捕获的异常: 竞拍价必须是数值,不能包含其他字符!
raise不需要参数
在使用raise语句时可以不带参数,此时raise语句处于except块中,它将会自动引发当前上下文激活的异常,否则,通常默认引发RuntimeError异常。
class AuctionException(Exception): pass
class AuctionTest:
def __init__(self,init_price):
self.init_price = init_price
def bid(self,bid_price):
d = 0.0
try:
d = float(bid_price)
except Exception as e:
print("转换出异常:",e)
#再次引发自定义异常
raise
if self.init_price > d:
raise AuctionException("竞拍价比起拍价低,不允许竞拍!")
init_price = d
def main():
at = AuctionTest(20.4)
try:
at.bid("df")
except AuctionException as ae:
#再次捕获到bid()方法中的异常,并对该异常进行处理
print('main函数捕获的异常:',type(ae))
main()
运行结果:
ValueError: could not convert string to float: 'df'
转换出异常: could not convert string to float: 'df'
python的异常传播轨迹
异常对象提供了一个with_traceback用于处理异常的传播轨迹
例:
class SelfException(Exception):
pass
def main():
firstMethod()
def firstMethod():
secondMethod()
def secondMethod():
thirdMethod()
def thirdMethod():
raise SelfException("自定义异常信息")
main()
运行结果:
python专门提供了traceback模块来处理异常传播轨迹,导入traceback模块之后,traceback提供了两个常用的方法
1、traceback.print_exc():将异常传播轨迹信息输出到控制台或指定文件中
2、format_exc():将异常传播轨迹信息转换成字符串
例:
#导入traceback模块
import traceback
class SelfException(Exception):
pass
def main():
firstMethod()
def firstMethod():
secondMethod()
def secondMethod():
thirdMethod()
def thirdMethod():
raise SelfException("自定义异常信息")
try:
main()
except:
#捕获异常,并将异常传播信息输出到控制台
traceback.print_exc()
#捕获异常,并将异常传播信息输出到指定文件中
traceback.print_exc(file=open('log.txt','a'))
运行结果:
异常处理规则
异常处理的目的:
1、使程序代码混乱最小化
2、捕获并保留诊断信息
3、通知相关人员
4、采用合适的方法结束异常
异常处理规则:
1.不要过度使用异常
不要把异常和普通错误混淆在一起,不再编写任何错误处理代码,而是以简单地 发异常来代替所有的错误处理。
不要使用异常处理来代替正常的程序流程控制。
2.不要使用庞大的try块
当try块过于庞大时,就难免在 try 块后紧跟大量的 except 块才可以针对不同的异常提供不同的处理逻辑。在同 try 块后紧跟大量 except 块则需要分析他们之间的逻辑关系,反而增加了编程复杂度 。
正确的做法是,把大块的 try 块分割成多个可能出现异常的程序段落,并把它们放在单独的 try块中,从而分别捕获并处理异常。
3.不要忽略捕捉到的异常,可以对异常采取适当措施。