Python学习教程(八)——异常

1. 异常的概念

  在编写程序时,程序员通常需要辨别事件的正常过程和异常(非正常)的情况。这类异常事件可能是错误(比如试图除以0),或者是不希望经常发生的事情。为了能够处理这些异常事件,可以在所有可能发生这类事件的地方都使用条件语句。但是这么做可能不仅会没效率和不灵活,而且还会让程序难以阅读。你可能会想直接忽略这些异常事件,期望他们永不发生,但Python的异常对象提供了非常强大的替代解决方案。
  Python用异常对象(exception object)来表示异常情况。遇到错误后会引发异常。如果异常对象未被处理和捕捉,程序就会用所谓的回溯(traceback,一种作为信息)终止执行:

>>> 1/0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

  事实上,每个异常都是一写类的实例,本例中是ZeroDivisionError的实例,这些实例可以被引发,并且可以用很多中方法进行捕捉,使得程序可以捉住错误并对其进行处理,而不是让整个程序失效。

2. 按自己的方式出错

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

2.1. raise语句

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

>>> raise Exception
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Exception
>>> raise Exception('hyperdrive overload')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Exception: hyperdrive overload

  第一个例子raise Exception引发了一个没有任何有关错误信息的普通异常。后一个例子中,则添加了错误信息hyperdrive overload
  内建的异常类有很多,一些重要的异常类如下:

类名描述
Exception所有异常的基类
AttributeError特性引用或赋值失败时引用
IOError试图打开不存在文件(包括其他情况)时引发
IndexError在使用序列中不存在的索引时引发
KeyError在使用映射中不存在的键时引发
NameError在找不到名字(变量)时引发
SyntaxError在代码为作为形式时引发
TypeError在内建操作或者函数应用于错误类型的对象时引发
ValueError在内建操作或者函数应用于正确类型的对象,但是该对象使用不合适的值时引发
ZeroDivisionError在除法或者模除操作的第二个参数为0时引发

2.2. 自定义异常类

  尽管内建的异常类已经包含了大部分情况,但是有时候我们还是需要创建自己的异常类,以更明确的显示异常的信息,比如,假设有一个商城网站,在调用支付服务时如果出现异常,我们需要自定一个PaymentError异常类以明确的说明异常的情况,并且可以根据异常所在的类,选择性的处理当前类型的异常(相关内容见下一节的捕捉异常)。所以如果想要使用特殊的错误处理代码处理支付时的错误,那么就需要一个独立于exception模块的异常类。自定义一个异常类如下:

class PaymentError(Exception): pass

上面的例子,可以根据实际需要增加方法。

3. 捕捉异常

  针对每一种异常,我们可以处理它们(我们称其为捕捉异常),用try/except来实现,比如像这样:

x = input('Enter the first number:')
y = input('Enter the second number:')
print x/y

假设我们输入:10和0
程序输出:

Enter the first number:10
Enter the second number:0
Traceback (most recent call last):
  File "D:\workspace_oxygen\pythonTest\com\yanglh\Test.py", line 3, in <module>
    print x/y
ZeroDivisionError: integer division or modulo by zero

程序提示不能除以0,并且报了一个异常信息,现在我们捕捉这个异常,并做一些处理:

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 "

假设我们输入:10和0
程序输出:

Enter the first number:10
Enter the second number:0
The second number can't be zero

  我们看,程序捕捉到了这个异常,并且做了友好的处理,打印了一行提示。

如果没有捕捉异常,它就会被传播到调用的函数中。如果在那里依然没有捕获,这些异常就会“浮”到程序的最顶端。也就是说你可以捕捉到在其他人的函数中所引发的异常。


异常的传递
  假如捕捉到了异常,并做了处理,但是还想再重新引发它,并往上一层抛出,那么可以使用raise语句,比如:

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 "
    raise

假如,输入10和0
程序输出:

Enter the first number:10
Enter the second number:0
The second number can't be zero 
Traceback (most recent call last):
  File "D:\workspace_oxygen\pythonTest\com\yanglh\Test.py", line 4, in <module>
    print x/y
ZeroDivisionError: integer division or modulo by zero

  我们看到,上面的程序,当除以0时,已经捕捉到了异常,并且进行打印处理,但是还是把这个异常又往上一层抛出了。其实我们已经注意到了raise语句后面没有任何东西,这就表示把捕捉到的异常类型原封不动的抛给了上一层。 那么我们可不可以抛出一个自定义的异常类型呢?答案是可以的,看下面的例子:

class MyZeroDivisionError(Exception): pass

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 "
    raise MyZeroDivisionError

假设,输入10和0
程序输出:

Enter the first number:10
Enter the second number:0
The second number can't be zero 
Traceback (most recent call last):
  File "D:\workspace_oxygen\pythonTest\com\yanglh\Test.py", line 9, in <module>
    raise MyZeroDivisionError
__main__.MyZeroDivisionError

  我们会看到,我们首先自定义了一个MyZeroDivisionError异常,然后当除以0时,则抛出了我们自定义的异常类。

4. 多个except子句的使用

  还是以上一节的例子说明,当我们第二个参数输入0时,程序报了ZeroDivisionError异常,但是当我们第二个参数输入一个字符串呢?它必然会报出一个TypeError异常。针对这种可能会出现多种类型的异常,我们应该怎么捕捉呢?这时就可以使用多个except子句:

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 "
except TypeError:
    print "That wasn't a number"

假设,输入10和“hello”
程序输出:

Enter the first number:10
Enter the second number:"hello"
That wasn't a number

上面的例子,用了两个异常类,当任何一个异常类型发生时,就会被其中一个对应的except子句所捕捉到。

5. 一个except捕捉多个异常

  如果需要用一个except捕捉多个异常,可以像下面这样:

try:
    x = input('Enter the first number:')
    y = input('Enter the second number:')
    print x/y
except (ZeroDivisionError,TypeError):
    print "Your numbers were bogus... "

注意多个异常类外面一定加上括号。

6. 捕捉对象

  如果希望在except子句中访问异常对象本身,我们可以像下面这样做:

try:
    x = input('Enter the first number:')
    y = input('Enter the second number:')
    print x/y
except (ZeroDivisionError,TypeError),e:
    print e

  参数e就是异常对象本身,就算是捕捉到多个异常,也只需要想except子句提供一个参数(元组)。

在Python3.0中,except子句会被写作except(ZeroDivisionError,TypeError) as e

7. 异常全捕捉

  如果捕捉异常时并不知道会有什么异常会发生,那么可以所有异常全捕捉,就像下面的例子那样;

try:
    x = input('Enter the first number:')
    y = input('Enter the second number:')
    print x/y
except Exception ,e:
    print e

Exception类可以捕捉到所有类型的异常,或者也可以像下面这样:

try:
    x = input('Enter the first number:')
    y = input('Enter the second number:')
    print x/y
except :
    print "exception!"

并不推荐第二种方式。

8. try…except…else子句

  假如我们希望在没有任何异常发生时,再最后执行一段逻辑,我们可以用else子句,向下面这样:

try:
    doSomething()
except:
    doSomething_except()
else:
    doSomething_else()

  当上面的例子中没有异常发生时,会执行else子句。

9. finally子句

  finally子句表示肯定会被执行的子句代码块(不管有没有异常发生),如下:

try:
    x = input('Enter the first number:')
    y = input('Enter the second number:')
    print x/y
finally:
    print "finally !"

  上面的例子,不管有没有异常发生,finally块的代码都会被执行。另外还可以try...except...finallytry...except...else...finally这种句式来使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值