首页 > 代码库 > 第8章 异常

第8章 异常

  在编写程序时,程序眼通常需要分辨时间的正常过程和异常(非正常)的情况。这类异常事件可能是错误(比如试图除以0),或者是不希望经常发生的事情。为了能够处理这些异常事件,可以在所有可能发生的这类事件的地方使用条件语句(比如让程序检查除法的分母是否为0)。但是,这么做可能不仅会没有效率和不灵活,而且还会让程序难以阅读。你可能回直接忽略这些异常事件,期望他们永远都不要发生,但python的异常对象提供了非常强大的替代解决方案。本章介绍如何创建和引发自定义的异常,以及处理异常的各种方法。

 8.1 什么是异常

python用异常对象(exception object)来表示异常情况。遇到错误后,会引发异常。如果异常对象并未被处理和捕捉,程序就会用所谓的回溯(traceback,一种错误信息)终止执行。

>>>1/0

Traceback (most recent in last):

  File "<stdin>",line 1, in ?

ZeroDivisionError: integer division or modulo by zero

如果这些错误信息就是异常功能的全部,那么它就是没必要存在了。事实上,每个异常都是一些类(本例中是ZeroDivisionError)的实例,这些实例可以被引发,并且可以用很多种方法进行捕捉,使得程序可以捉住错误并且对其进行处理,而不是让整个程序失效。

8.2按自己的方式出错

异常可以在某些东西出错时自动引发。在学习如何处理异常之前,先看下自己如何引发异常,以及创建自己的异常类型。

8.2.1raise语句

为了引发异常,可以使用一个类(应该是Exception)或者实力参数调用raise语句。使用类时,程序会自动创建类的一个实例。下面是一些简单地例子,使用了内建的Exception异常类:

>>>raise Exception 

Traceback (most recent call last )

  File "<stdin>", line 1, in ?

Exception

>>>raise Exception(‘hyperdrive overload‘)

Traceback (mosr recent call last)

   File "<stdion>", line 1, in ?

Exception: hypedrive overload

第一个例子raiseException引发了一个没有任何有关错误信息的普通异常。后一个例中,则添加了错误信息hypedrice overload

内建的异常种类有很多,python库参考手册的Bulit-in Exception一节中有关于它的描述。用交互式解释器也可以分析他们,这些内建异常都可以在exceptions模块(和内建的命名空间)中找到。可以使用dir函数列出模块的内容,这部分内容会在第10章中讲到:

>>> import exceptions

>>>dir(exceptions)

[‘AirthmeticError‘,‘AssertionError‘,‘AttributeError‘,...]

读者的解释器中,这个名词可能要长得多——出于对易读性的考虑,这里删除了大部分名字。所有这些异常都可以用在raise语句中:

>>>raise ArirthmetricError

Traceback (most recent call last):

   File “<stdin>”,  line 1, in ?

ArithmeticError

 8.2.2自定义异常类

尽管自定义的异常类已经包括了大部分情况,而且对于很多要求都已经足够了,但有些时候还是需要常见自己的异常类。比如在超光速推进装置过载(hpyerdrived overload)的例子中,如果能有一个具体的HyperDriveError类来表示超光速推进装置的错误状况是不是更自然一些呢?错误信息是足够了,但在8.8节中看到,可以根据异常所在的类,选择性处理当前类型的异常。所以如果想要使用使用特殊的错误代码出里超光速推进装置的错误,那么就需要一个独立于exceptions模块的异常类。

那么如何创建自己的异常类呢?就像其他类一样,只要确保从Exception类继承(不管是间接的还是直接的,也就是说继承其他的内建异常类也是可以的)。那么编写一个自定义异常类基本上就像下面这样:

class SomeCustomExcption(Exception):pass

还不能做太多事,对吧?(如果你原因,也可以向你的异常类中增加方法。)

8.3捕捉异常

前面曾经提到过,关于异常的最有意思的地方就是可以处理他们(通常叫做诱捕或者捕捉异常)。这个功能可以使用try/ecxcept语句来实现。假设创建了一个让用户输入两个数,然后进行相除的程序,像下面这样:

x = input (‘Enter the first number: ‘)

y = input (‘Enter the secong number: ‘)

print x/y

程序工作正常,假如用户输入0作为第二个数

Enter the first number:  10

Enter the first number:0

Traceback (most the call last):

   File "exceptions.py",line 3, in ?

   print x/y

ZeroDivisionError: integer division or modulo by zero 

为了捕捉异常并且做出一些错误处理(本例中只是输出一些更友好的错误信息),可以这样重写程序:

try:

    x = input (‘Enter the first number: ‘)

    y = input (‘Enter the second number:‘)

    print x/y

except ZeroDivisionError:

   print "the second number can‘t be zero!"

看起来用if语句检查y值会更简单一些,本例中这样做的确很好,但是如果需要给程序加入更多除法,那么就得给没个除法加个if语句,而使用try/except的话只需要一个错误处理器。

注意:如果没有捕捉异常,它就会被“传播”到调用函数中。如果在那里依然没有捕获,这些异常就会浮到程序的最顶层。也就是说你可以捕捉到在其他的函数中所引发的异常。有关这方面的信息,请参见8.10节。

看,没参数

如果捕捉到了异常,但是又想重新引发它(也就是说要传递异常,不进行处理),那么可以调用不带参数的raise(还能在捕捉到异常时显式地提供异常,在8.6节会对次进行解释)。

举个例子吧,看看这么做多有用:考虑一下一个能“屏蔽”ZeroDivisionError(除零错误)的计算器类。如果这个行为被激活,那么计算器就会打印错误信息,而不是让异常传播。如果在于用户进行交互式的过程中使用,那么这就有用了,但是如果是在程序内部使用,引发异常会更好些。因此“屏蔽”机制就可以关掉了,下面是这样一个类的代码:

class  MuffledCalculator:

     muffed = False

     def calc(self ,expr):

          try:

              return eval (expr)

          excpet  ZeroDivisionError:

                if self.muffled:

                    print ‘Division by zero is illegal‘

                else:

                       raise

注意:如果除零行为发生而屏蔽机制被打开,那么calc方法会(隐式地)返回None。换句话说,如果你打开了屏蔽机制,那么就是不应该依赖返回值。

下面是这个类的用法示例,分别打开和关闭了屏蔽:

 >>>calculator = MuffledCalculator()

>>>calculator.calc(‘10/2‘)

5

calculator.calc(‘10/0‘) #No muffing

Traceback (most recent  call last ):

    File "<stdin>", line 1, in ?

    File "MuffledCalclator.py", line 6, in calc

        return eval (expr)

    File"<string>"   line  0,  in?

ZeroDivisionError: integer division or modulo by zero

>>>calculator.muffled = True

>>>calculator.calc(‘10/0‘)

Division by zero is illegal

 当计算器没有打开屏蔽机制时,ZeroDivisionError被捕捉但已传递了。

8.4不止一个except子句

如果运行上一节的程序并且在提示符后面输入非数字类型的值,就会产生另外一个异常:

Enter the first number: 10

Enter the second number: “Hello,world!”

Traceback(most recent call last)

 

第8章 异常