第8章 异常
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第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章 异常的主要内容,如果未能解决你的问题,请参考以下文章
编写高质量代码:改善Java程序的151个建议(第8章:异常___建议110~113)