首页 > 技术文章 > 《python学习手册》第34章 异常对象

jiaxin359 2017-09-25 09:30 原文

基于字符串的异常

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语句中,捕捉其超类就会捕捉这个类,以及类树中超类下的所有子类。

 

推荐阅读