一、声明
本文为转载,内容有修改,转载需遵守:转载前文章版权
二、什么是异常
程序运行的过程中,会出现错误,错误是不可避免的,常见的错误有三类:
第一种,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时出错,至此,找到错误源头。
出错的时候,一定要分析错误的调用栈信息,才能定位错误的位置。