异常

一.什么是异常
Python 用异常对象来表示异常情况。遇到错误后,就会引发异常。如果异常对象并未被处理或捕捉,程序就会用所谓的回溯终止执行:

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

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

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

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

内建的异常类有很多,下表描述了一些最重要的内建异常类:

类名——–描述
Exception 所有异常的基类
AttributeError 特性引用或赋值失败时引发
IOError 试图打开不存在文件(包括其他情况)时引发
IndexError 在使用序列中不存在的索引时引发
KeyError 在使用映射中不存在的键时引发
NameError 在找不到名字(变量)时引发
SyntaxError 在代码为错误形式时引发
TypeError 在内建操作或者函数应用于错误类型的对象时引发
ValueError在内建操作或者函数应用于正确类型的对象,但是该对象使用不合适的值时引发
ZeroDivisionError 在除法或者模除操作的第二个参数为0时引发
2.捕捉异常
关于异常最有意思的地方就是可以处理它们(通常叫做诱捕或者捕捉异常)。这个功能可以使用try/except语句来实现。假设创建了一个让用户输入两个数,然后进行相除的程序,像下面这样:

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

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

>>>Enter the first number: 10
>>>Enter the second number: 0
>>>Traceback (most recent 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的话就需要一个错误处理器。

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

3.不止一个except子句
如果运行一个程序并且在提示符后面输入非数字类型的值,就会产生另外一个异常:

>>>Enter the first number: 10
>>>Enter the second number: "Hello,world!"
>>>Traceback (most recent call last):
>>>    File "exceptions.py",line 4, in ?
>>>        print(x/y)
>>>TypeError: unsupported operand type(s) for /: 'int' and 'str'

因为except子句只寻找ZeroDivisionError异常,这次的错误就溜过了检查并导致程序终止。为了捕捉这个异常,可以直接在同一个try/except语句后面加上另一个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, was it?")

这次用if 语句实现可就复杂了。怎么检查一个值是否能被用在除法中?方法很多,但是目前最好的方式就是直接将值用来除一下看看是否奏效。
还应该注意到,异常处理并不会搞乱原来的代码,而增加一大堆if语句检查可能的错误情况会让代码相当难读。

4.用一个块捕捉两个异常
如果需要用一个块捕捉多个类型的异常,那么可以将它们作为元组列出,像下面这样:

>>>try:
>>>    x = input('Enter the first number:')
>>>    y = input('Enter the second number:')
>>>
>>>    print(x/y)
>>>except (ZeroDivisionError, TypeError,NameError):
>>>    print('Your numbers were bogus...')
上面的代码中,如果用户输入字符串或者其他类型的值,而不是数字,或者第2个数是0,都会打印同样的错误信息。当然,只打印一个错误信息并没什么帮助。另外一个方案就是继续要求输入数字直到可以进行除法运算为止。
注意,except子句中异常对象外面的圆括号很重要。忽略它们是一种常见的错误,那样你会得不到想要的结果。

三.捕捉对象
如果希望在except子句中访问异常对象本身,可以使用两个参数(注意,就算要捕捉到多个异常,也只需向except子句提供一个参数————一个元组)。比如,如果想让程序继续运行,但是又因为某种原因想记录下错误(比如只是打印给用户看),这个功能就很有用。
下面的示例程序就会打印异常(如果发生的话),但是程序会继续运行:

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

四.finally子句
最后,是finally 子句。它可以用来在可能的异常后进行清理。它和try子句联合使用:

>>>x = None
>>>try:
>>>    x = 1/0
>>>finally:
>>>    print('Cleaning up...')
>>>    del (x)

上面的代码中,finally子句肯定会被执行,不管try子句中是否发生异常(在try子句之前初始化x的原因是如果不这样做,由于ZeroDivisionError的存在,x就永远不会被赋值。这样就会导致在finally子句中使用del删除它的时候产生异常,而且这个异常是无法捕捉的)。
运行这段代码,在程序崩溃之前,对于变量x的清理就完成了:

>>>Cleaning up...
>>>Traceback (most recent call last):
>>>    File"C:\python\div.py",line4, in ?
>>>        x = 1/0
>>>ZeroDivisionError: integer division or modulo by zero 

因为使用del语句删除一个变量是非常不负责任的清理手段,所以finally子句用于关闭文件或者网络套接字时会非常有用。还可以在同一个语句中组合使用try’,except,finally和else(或者其中3个)。

>>>try:
>>>    1/0
>>>except NameError:
>>>    print("Unknown variable")
>>>else:
>>>    print("That went well!")
>>>finally:
>>>    print("Cleaning up.")

五.异常和函数
异常和函数能很自然地一起工作。如果异常在函数内引发而不被处理,它就会传播至(浮到)函数调用的地方。如果在那里也没有处理异常,它就会继续传播,一直到达主程序(全局作用域)。如果那里没有异常处理程序,程序会带着栈跟踪中止。
看个例子:

>>>def faulty():
...    raise Exception('Something is wrong')
...
>>>def ignore_exception():
...    faulty()
...
>>>def handle_exception():
...    try:
...        faulty()
...    except:
...        print('Exception handle')
...
>>> ignore_exception()
Traceback (most recent call last):
    File '<stdin>',line 1, in ?
    File '<stdin>',line 2, in ignore_exception
    File '<stdin>',line 2, in faulty
Exception: Something is wrong
>>>handle_exception()
Exception handled

可以看到,faulty中产生的异常通过faulty和ignore_exception传播,最终导致了栈跟踪。同样的,它也传播了handle_exception,但在这个函数中被try/except语句处理。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值