Python 第七章 异常
1.异常概述
异常机制主要依靠try、except、else、finally、raise五个关键字。其中在try后缩进的代码简称try块,里面放置的是可能引发异常的代码;except后对应的是异常类型和一个代码块,用于表明该except块处理这种类型的代码块;在多个except块之后可以放一个else块,表面程序不出现异常时还要执行else块;最后还可以跟一个finally块,用于回收在try块里打开的物理资源,异常机制会保证finally块总被执行;而raise用于引发一个实际的异常,可以单独作为语句使用,引发一个具体的异常对象。
2.使用try。。except捕获异常
try:
#业务实现代码
........
except (error1,error2,error3)as e:
alert 输入不合法
goto retry
如果在执行try块时出现异常,系统自动生成一个异常对象,被交给python解释器,这个过程叫引发异常。
Python解释器收到异常对象时会寻找能处理该异常对象的except块,如果找到合适的except块就交给它处理,这个过程叫捕获异常。
如果找不到捕获异常的except块,则运行环境终止,python解释器将退出。
3.异常类的继承体系
当Python解释器接收到异常对象后会依次判断该异常对象是否是except块后的异常类或其子类的实例(isinstance(ex,Exception1)),如果是,将调用该except块来处理该异常;否则,再次拿该异常对象和下一个except块的异常类进行比较。。
python的所有异常类都从BaseException派生而来,提供给了很多异常类,这些类之间有严格的继承关系。
import sys
try:
a = int(sys.argv[1])
b = int(sys.argv[2])
c = a/b
print('相除结果:',c)
except IndexError:
print('索引错误:运行程序时输入的参数个数不够')
except ValueError:
print('数值错误:程序只能接收整数参数')
except ArithmeticError:
print('算术错误')
except Exception:
print('未知异常')
sys模块的argv列表来获取运行python程序时提供的参数。sys.argv[0]通常代表正在运行的python程序名,sys.argv[1]代表运行程序时所提供的第一个参数,sys.argv[2]代表第二个参数。。。
通常把except块放在最后,先处理小异常,再处理大异常。
4.多异常捕获
import sys
try:
a = int(sys.argv[1])
print(a)
b = int(sys.argv[2])
print(b)
c = a/b
print('相除结果:',c)
except (IndexError,ValueError,ArithmeticError):
print('程序发生了数组越界、数字格式错误、算数异常之一。')
except :
print('未知异常')
11行只有except关键字,并未指定具体要捕获的类型,它表示可捕获所有类型的异常,一般作为异常捕获的最后一个except块。
5.访问异常信息
如果程序需要在except块中访问异常对象的相关信息,可以通过为异常对象声明变量实现。当python解释器决定调用某个except块来处理该异常对象时,会将异常对象赋值给except块后的异常变量,程序可通过该变量获得异常对象的相关信息。
所有异常对象都包含以下几个常用属性和方法。
- args:返回异常的错误编号和描述字符串。
- errno:返回异常的错误编号。
- strerror:返回异常的描述字符串。
- with_traceback():处理异常的传播轨迹信息。
def foo():
try:
fis = open('a.txt');
except Exception as e:
print(e.args)
print(e.errno)
print(e.strerror)
foo()
# (2, 'No such file or directory')
2
No such file or directory
r如果要访问异常对象,只要在单个异常类或异常类元组后面加上as再加上异常变量即可。
6.else块
当try块没有出现异常时,会执行else块。一般来说,大部分代码没有else,都直接放在了try后面,但是当try无异常,而else块有异常时就能体现else块的作用。
s = input('输入除数:')
try:
result = 20 / int(s)
print('20/%s的结果是:%g' % (s,result))
except ValueError:
print('值错误,重新输入')
except ArithmeticError:
print('算术错误,不能输入0')
else:
print('无异常')
一般来说,大部分代码没有else,都直接放在了try后面,但是当try无异常,而else块有异常时就能体现else块的作用。
def else_test():
s = input('请输入除数:')
result = 20 / int(s)
print('20除以%s的结果是: %g' % (s , result))
def right_main():
try:
print('try块的代码,没有异常')
except:
print('程序出现异常')
else:
# 将else_test放在else块中
else_test()
def wrong_main():
try:
print('try块的代码,没有异常')
# 将else_test放在try块代码的后面
else_test()
except:
print('程序出现异常')
wrong_main()
right_main()
right_main将else_test()放在else后面;wrong_main放在了try里面。
当try和else块都没有异常时,放在哪都没有区别。
但是如果输入的数据让else_test()出现异常(try块没有异常),此时程序会报错:都输入0,这样都会使else_test出现异常,如果将其放在try后面,出现的异常会被try对应的except捕获。但如果放在else块中,没有except来处理,所以会报错。
所以如果希望异常能被except捕获,就放在try中,如果希望异常能向外传播,就放在else中。
7.使用finally回收资源
有时候程序在try里打开了一些物理资源,比如数据库连接、网络连接、磁盘文件,这些物理资源必须显示回收。
import os
def test():
fis = None
try:
fis = open("a.txt")
except OSError as e:
print(e.strerror)
# return语句强制方法返回
return # ①
# os._exit(1) # ②
finally:
# 关闭磁盘文件,回收资源
if fis is not None:
try:
# 关闭资源
fis.close()
except OSError as ioe:
print(ioe.strerror)
print("执行finally块里的资源回收!")
test()
#
No such file or directory
执行finally块里的资源回收!
在9行有return,一般情况下,一旦方法执行到return语句,程序会立即结束该方法,现在不会了,会先执行finally里的代码再强制结束方法。
通常情况下,不要在finally块中使用如return或raise等导致方法中止的语句,一旦用了将会导致try块、except块中的return、raise语句失效。
程序在执行try块、except块时遇到了return、raise语句,这两个语句都会使方法立即结束,程序不会立即结束而是会去找该异常处理流程中的finally块,如果没有找到会立即结束方法,找到了立即执行finally,结束后再执行try和except,如果finally中用了return和raise等导致程序中止的语句,finally块就已经终止了方法,不会再去执行try、except。
8.使用raise引发异常
如果需要在程序中自行引发异常,要使用raise,有三种用法:
- raise:单独一个raise,引发当前上下文中捕获的异常,或默认引发RuntimeError异常。
- raise 异常类:raise后带一个异常类。引发指定异常类的实例。
- raise 异常对象:引发指定的异常对象。
上面三种用法最终都要引发一个异常实例,raise每次引发一个实例。
import traceback
def main():
try:
# 使用try...except来捕捉异常
# 此时即使程序出现异常,也不会传播给main函数
mtd(3)
except Exception as e:
print('程序出现异常:', e)
# help(e.with_traceback)
traceback.print_exc()
# e.with_traceback(e)
# 不使用try...except捕捉异常,异常会传播出来导致程序中止
mtd(3)
def mtd(a):
if a > 0:
raise ValueError("a的值大于0,不符合要求")
main()
程序在调用mtd(3)时有异常,被except捕获,第二次调用mtd(3)时没有被except捕获,会一直向上传播被python解释器终止程序。
9.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("竞拍价必须是数值,不能包含其他字符!") # ①
raise AuctionException(e)
if self.init_price > d:
raise AuctionException("竞拍价比起拍价低,不允许竞拍!")
initPrice = 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函数捕捉的异常: could not convert string to float: 'df'
except捕获到异常后,系统打印了该异常的字符串信息,接着引发一个AuctionException异常,通知该方法的调用者处理该AE异常。14行就是把原始异常e包装成了AE异常,这种方式也叫异常包装;14行也可以不用参数,只用一个raise,会再次引发except捕获的异常。
10.异常传播轨迹
异常对象提供了一个with_traceback用于处理异常的传播轨迹,查看异常的传播轨迹可追踪异常触发的源头,也可看到异常一路触发的轨迹。
class SelfException(Exception): pass
def main():
firstMethod()
def firstMethod():
secondMethod()
def secondMethod():
thirdMethod()
def thirdMethod():
raise SelfException("自定义异常信息")
main()
Traceback (most recent call last):
File "D:/python_work/GUI/基础.py", line 11, in <module>
main()
File "D:/python_work/GUI/基础.py", line 4, in main
firstMethod()
File "D:/python_work/GUI/基础.py", line 6, in firstMethod
secondMethod()
File "D:/python_work/GUI/基础.py", line 8, in secondMethod
thirdMethod()
File "D:/python_work/GUI/基础.py", line 10, in thirdMethod
raise SelfException("自定义异常信息")
__main__.SelfException: 自定义异常信息
从最下面一行往上触发。
traceback模块可以处理异常传播轨迹。
# 导入trackback模块
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'))
Traceback (most recent call last):
File "D:/python_work/GUI/基础.py", line 14, in <module>
main()
File "D:/python_work/GUI/基础.py", line 6, in main
firstMethod()
File "D:/python_work/GUI/基础.py", line 8, in firstMethod
secondMethod()
File "D:/python_work/GUI/基础.py", line 10, in secondMethod
thirdMethod()
File "D:/python_work/GUI/基础.py", line 12, in thirdMethod
raise SelfException("自定义异常信息")
SelfException: 自定义异常信息
11.异常处理规则
- 不要过度使用异常
- 不要使用过于庞大的try块
- 不要忽略捕获到的异常