《python学习手册》第34章 异常对象
基于字符串的异常
python在2.6之前可以使用字符串来定义异常,并且是通过对象标识符来匹配的(即通过is 而不是==)
myexc = "My excetion string" try: raise myexc except myexc: print('caught')
基于类的异常
字符串定义的异常非常简单,但是并不容易维护。使用类定义的异常通过超类关系进行匹配,只要except列举出来的异常的类或者任何超类名,引发的异常都会匹配到。此外,类的异常还支持异常状态信息,可以让异常参与继承层次。
类异常的例子
class General(Exception):pass class Special1(General):pass class Special2(General):pass def raise0(): raise General() def raise1(): raise Special1() def raise2(): raise Special2() for fun in (raise0,raise1,raise2): try: fun() except General: import sys print('caught',sys.exc_info()[0])
运行的结果如下
caught <class '__main__.General'> caught <class '__main__.Special1'> caught <class '__main__.Special2'>
将excep的部分化为下面的内容,结果一样,sys.exc_info将在下一章讲到。
except General as x: print('caught:',x.__class__)
类异常的另一个例子
假如我们维护了一个mathlib.py的库,原本定义了两个异常Divzero和Oflow,
class Divzero(Exception): pass class Oflow(Exception): pass def func(): raise Divzero()
当别人使用我们的库的时候我们可以进行异常处理:
import mathlib try: mathlib.func() except (mathlib.Divzero,mathlib.Oflow): '''do what you want to do '''
但是,当我们维护我们的库的时候新加入一个异常Uflow的时候,别人必须修改他们的代码,这样在大型程序当中非常的不好。所以我们尝试把我们库中的代码写成下面的形式:
class NumErr(Exception):pass class Divzero(NumErr): pass class Oflow(NumErr): pass '''其余的异常''' def func(): raise Divzero()
别人引用的时候只需要写成下面的形式即可:
import mathlib try: mathlib.func() except mathlib.NumErr: '''do what you want to do '''
无论我们增加多少个异常的类,别人的代码都不需要进行修改。
内置Exception类
-BaseException类:异常的顶级根类,不可以被用户定义的类直接继承(应当使用Exception)。它提供了子类所继承的默认的打印和状态保持行为。
-Exception类:与应用相关的异常的顶层根超类,是BaseExcption的一个直接子类,并且是所有其他内置异常的超类,除了系统退出事件类以外(SystemExit、KeyboardInterrupt和GeneratorExit)。
In [2]: Exception.__bases__ Out[2]: (BaseException,)
In [3]: SystemExit.__bases__ Out[3]: (BaseException,)
-ArithmeticeError 所有数值错误的超类
In [4]: ArithmeticError.__bases__ Out[4]: (Exception,)
-OverflowError 识别特定的数值错误的子类
In [5]: OverflowError.__bases__ Out[5]: (ArithmeticError,)
从上面的代码可以看出,python中的异常总是这样的进行继承的
默认打印和状态
python内置异常提供默认的打印方法:传递给这些类的任何构造函数参数都会保存在实例的args元祖属性中,并且当打印该实例的时候自动显示。这就说明了为什么传递给内置异常类的参数会出现在出错消息中。
输入:
raise IndexError
输出:
Traceback (most recent call last): File "D:\application\eclipse\workspace\yichang\c3\t6.py", line 1, in <module> raise IndexError IndexError
输入:
raise IndexError('nihao')
输出:
Traceback (most recent call last): File "D:\application\eclipse\workspace\yichang\c3\t6.py", line 1, in <module> raise IndexError('nihao') IndexError: nihao
输入:
i = IndexError('nihao') print(i.args)
输出:
('nihao',)
同样,我们自己定义的类也是如此,因为他们继承了内置超类中存在的构造很熟和显示方法
输入:
class MyErr(Exception):pass try: raise MyErr('nihao') except MyErr as x: print(x,x.args)
输出:
nihao ('nihao',)
输入:
class MyErr(Exception):pass try: raise MyErr('nihao','wohao','dajiahao') except MyErr as x: print(x,x.args)
输出:
('nihao', 'wohao', 'dajiahao') ('nihao', 'wohao', 'dajiahao')
为什么输出x和x.args是一样的?因为,这是由于__str__来决定的,直接输出了str(args)。
定制我们自己的打印显示:
其实,主要是修改我们定制的类中的__str__和__repr__方法,而__str__又是主要的。
下面我们为我们自己定义的类当中重定义__str__方法:
class MyErr(Exception): def __str__(self): return 'this is MYErr Exception' try: raise MyErr('nihao','wohao','dajiahao') except MyErr as x: print(x)
输出: this is MYErr Exception
改造构造方法
class FormatError(Exception): def __init__(self,line,file): self.line = line self.file = file try: raise FormatError(2,'nihao') except FormatError as x: print(x.line,x.file) print(x.args[0],x.args[1])
通过改造构造方法,可以体统更多的细节,但是args还是可以使用的,输出结果如下:
2 nihao
2 nihao
定义一些方法,在处理器内部执行
class FormatError(Exception): logfile = r'log.txt' def __init__(self,line,file): self.line = line self.file = file def logger(self): log = open(self.logfile,'a') print('error at line:',self.line,'file:',self.file,file=log) try: raise FormatError(11,'t7.txt') except FormatError as x: x.logger()
该异常定义了出错的时候存入特定文件夹的功能,当捕获异常的时候,执行logger方法
执行后在log.txt中有如下语句: error at line: 11 file: t7.txt
总结:这一章我们其实是明确了,异常主要是通过类来实现的,在try语句中,捕捉其超类就会捕捉这个类,以及类树中超类下的所有子类。