异常处理

一、声明

本文为转载,内容有修改,转载需遵守:转载前文章版权

二、什么是异常

程序运行的过程中,会出现错误,错误是不可避免的,常见的错误有三类:
第一种,bug,这种错误是因为程序编写问题造成的,比如应该求平均程序,但是求出的确实总成绩。bug是必须要修复的。
第二种,输入错误,这种一般是在期望输入值时产生的。比如期望用户输入email地址,但是用户输入的是手机号。这种情况可以通过对输入内容进行检查,做相应的处理。
第三种,程序运行以来环境错误,比如写入文件时,磁盘满了,写不进去。比如网络抓取数据,网络断了。这类错误也称为异常,在程序中通常是必须处理的,否则,程序会因为各种问题终止并退出。
以上内容来自廖雪峰的python3课程,具体内容参考文末链接。

三、异常处理

try….except….finally
python使用try….except….finally套关键字处理异常,下面使用一个例子来看看try如何使用(例子来自廖雪峰 python3课程):

#!/usr/bin/env python3  

try:  
    print('try...')  
    r = 10 / 0  
    print('result:', r)  
except ZeroDivisionError as e:  
    print('except:', e)  
else:  
    print('no error!')  
finally:  
    print('finally...')  
    print('END')   

如果某段代码要进行异常处理,则把这些代码都放到try块中。如果执行出错,则后续代码不会运行,而是跳转到except的代码块,执行except块中的代码。except中的代码是错误处理代码。比如上面的代码就是输出错误的原因。如果try代码块中执行没有出错,则会执行esle中的代码。else块不是必须的。finally快中的代码不论try快中代码是否出错,都会执行。finally块不是必须的。
上段代码中,r = 10 / 0,这里存在错误,即除数不能为0. ZeroDivisionError这个异常处理类,就是用来处理此异常的。
python系统中定义的错误类型,都是从BaseException类派生出来的,常见的错误类型和继承关系可以看官方的说明

四、多个异常处理

同一段代码中可能会有多个异常,这个时候使用多个except捕捉即可。

#!/usr/bin/env python3  

try:  
    print('try...')  
    r = 10 / int('a')  
    print('result:', r)  
except ValueError as e:  
    print('ValueError:', e)  
except ZeroDivisionError as e:  
    print('ZeroDivisionError:', e)  
else:  
    print('no error!')  
finally:  
    print('finally...')  
    print('END')  

上面的代码中,有两个except,其中一个捕获ValueError错误,另外一个捕获ZeroDivisionError错误。其中 r = 10 / int(‘a’)中int函数会抛出ValueError错误。

注意事项

Python的错误其实也是class,所有的错误类型都继承èªBaseException,所以在使用except时需要注意的是,它不但捕获该类型的错误,还把其子类也“一网打尽”。比如:

try:  
    foo()  
except ValueError as e:  
    print('ValueError')  
except UnicodeError as e:  
    print('UnicodeError')  

第二个except永远也捕获不到UnicodeError,因为UnicodeError是ValueError的子类,如果有,也被第一个except给捕获了。

五、抛出指定异常

python中使用raise可以抛出指定的异常。raise的用法参考下面的内容。

六、自定义异常

python中可以自定义异常类型。定义的方法是通过继承官方的异常类来自定义异常类型。下面通过一个代码说明:

#!/usr/bin/env python3
class MyInputError(Exception):
    """Exception raised when there're errors in input"""
    def __init__(self, value): # 自定义异常类型的初始化
        self.value = value
    def __str__(self): # 自定义异常类型的 string 表达形式
        return ("{} is invalid input".format(repr(self.value)))

try:
    raise MyInputError(1) # 抛出 MyInputError 这个异常
except MyInputError as err:
    print('error: {}'.format(err))

  程序执行结果:
error: 1 is invalid input

上面定义了MyInputError类,类继承Exception错误基础类。

七、多层调用

异常处理可以多层调用,可以看下面的程序。函数main()调用foo(),foo()调用bar(),结果bar()出错了,这时,只要main()捕获到了,就可以处理。
这样,就不需要在每个可能出错的地方去捕获错误信息,而是在合适的层次去捕获错误即可。

#!/usr/bin/env python3  

def foo(s):  
    return 10 / int(s)  

def bar(s):  
    return foo(s) * 2  

def main():  
    try:  
        bar('0')  
    except Exception as e:  
        print('Error:', e)  
    finally:  
        print('finally...')  

main()  

输出结果:  
Error: division by zero  
finally...  

八、调用栈

如果异常没有被捕获,它会一直往上抛,最后被python解释器捕获,会打印一个错误信息,然后程序退出。来看下下面的程序:

#!/usr/bin/env python3  

def foo(s):  
    return 10 / int(s)  

def bar(s):  
    return foo(s) * 2  

def main():  
    bar('0')  

main()  

程序运行结果:  
Traceback (most recent call last):
  File "yichang.py", line 12, in <module>
    main()
  File "yichang.py", line 10, in main
    bar('0')
  File "yichang.py", line 7, in bar
    return foo(s) * 2
  File "yichang.py", line 4, in foo
    return 10 / int(s)
ZeroDivisionError: division by zero

出错信息解读
错误的第一行,这一行告诉我们,下面是程序错误的跟踪信息。
Traceback (most recent call last):

第2~3行:
File “err.py”, line 12, in
main()

调用main()出错了,在代码文件err.py的第11行代码,但原因是第10行:
File “err.py”, line 10, in main
bar(‘0’)

调用bar(‘0’)出错了,在代码文件err.py的第9行代码,但原因是第7行:
File “err.py”, line 7, in bar
return foo(s) * 2

原因是return foo(s) * 2这个语句出错了,但这还不是最终原因,继续往下看:
File “err.py”, line 4, in foo
return 10 / int(s)

原因是return 10 / int(s)这个语句出错了,这是错误产生的源头,因为下面打印了:
ZeroDivisionError: integer division or modulo by zero

根据错误类型ZeroDivisionError,我们判断,int(s)本身并没有出错,但是int(s)返回0,在计算10 / 0时出错,至此,找到错误源头。
出错的时候,一定要分析错误的调用栈信息,才能定位错误的位置。

九、文章原地址:

廖雪峰python3

Previous Post

IO编程

Next Post

python程序调试

Related Posts