异常不可怕,关键是要去处理异常。有些异常是在编写中能够预料到的,比如如果想要让用户输入2个数字进行运算,那么就要考虑到用户可能会输入除数为0或者输入字符串的情况。
程序员在编写代码时要考虑到用户的这种可能引起异常的操作,从而通过相应的处理规避异常。
在之前的章节Python复合语句中我们就已经接触到try…except语句了。这个语句就是用来捕获异常的。在这里我们再复习一下。
try语句完整的命令包括:try、except、else和finally子语句
try子语句跟着的是可能会出现运行异常的命令。
except子语句跟着的是可能会出现的异常类型(可以有多个),捕获到了后就执行。
else子语句跟着的是try子语句没有出现异常将要执行的命令
finally子语句是一定要执行的命令。
捕获异常的要点:
1、所有的异常会一层一层地往上传递。
2、异常类型也是类,Exception异常是所有异常的基类(所有的异常继承于异常)。
3、如果不知道会产生什么异常,就可以用Exception这个基类,或者干脆不写异常类型,这将捕获所有的异常(可以这样做,但不推荐)。
4、其它异常类也有继承关系,一旦有except捕获到了,则其它的except子语句将不再继续捕获异常。因此在写except子语句顺序上必须要注意,范围小的异常类型一定要写在前面。
5、如果想知道异常的详细信息,可以使用traceback模块。使用方式为:traceback.print_exc()
异常继承
以下通过一些示例来对上面的要点进行说明:
异常地传递
def fun1():
print(1)
print(1/0)
def fun2():
print(2)
fun1()
fun2()
Traceback (most recent call last):
File “E:\BaiduNetdiskWorkspace\FrbPythonFiles\study\test1.py”, line 11, in
fun2()
File “E:\BaiduNetdiskWorkspace\FrbPythonFiles\study\test1.py”, line 8, in fun2
fun1()
File “E:\BaiduNetdiskWorkspace\FrbPythonFiles\study\test1.py”, line 3, in fun1
print(1/0)
ZeroDivisionError: division by zero
2
1
如上所示代码,第3行的print(1/0)报错传递给第8行的fun1(),然后再传递到第11行的fun2()。
用Exception捕获所有的异常
try:
1/0
except Exception:
print('catch')
catch
try:
print(b)
except Exception:
print('catch')
catch
不指定要捕获的异常类(默认捕获所有的异常)
try:
1/0
except:
print('catch')
catch
捕获时异常类型顺序问题
try:
1/0
except ArithmeticError:
print('arit')
except ZeroDivisionError:
print('zero')
arit
如上代码,ZeroDivisionError永远不会被捕获到,因为ZeroDivisionError是ArithmeticError子类,ArithmeticError永远是优先进行捕获。
在实际使用中,一定要将ZeroDivisionError捕获写在ArithmeticError前面。
获取异常的详细信息
import traceback
try:
100/0
except:
traceback.print_exc()
Traceback (most recent call last):
File “E:\BaiduNetdiskWorkspace\FrbPythonFiles\study\test1.py”, line 4, in
100/0
ZeroDivisionError: division by zero
raise 语句支持强制触发指定的异常。让我们来看2个Python官网的例子:
>>> raise NameError(‘HiThere’)
Traceback (most recent call last):
File “”, line 1, in
NameError: HiThere
raise 唯一的参数就是要触发的异常。这个参数必须是异常实例或异常类(派生自 Exception 类)。如果传递的是异常类,将通过调用没有参数的构造函数来隐式实例化:
>>> raise ValueError # shorthand for ‘raise ValueError()’
如果只想判断是否触发了异常,但并不打算处理该异常,则可以使用更简单的 raise 语句重新触发异常:
try:
raise NameError('HiThere')
except NameError:
print('An exception flew by!')
raise
An exception flew by!
Traceback (most recent call last):
File “”, line 2, in
NameError: HiThere
除了Python自带的异常外,开发者为了方便还可以自定义异常类。但是不论是以直接还是间接的方式,异常都应从 Exception 类派生。
异常类可以被定义成能做其他类所能做的任何事,但通常应当保持简单,它往往只提供一些属性,允许相应的异常处理程序提取有关错误的信息。
大多数异常命名都以 “Error” 结尾,类似标准异常的命名。
许多标准模块都需要自定义异常,以报告由其定义的函数中出现的错误。
自定义异常类
class myError(Exception):
pass
try:
print('test')
raise myError('found error')
except myError as err:
print('catch')
print(err)
test
catch
found error
通过自定义异常类,可以让我们在编写程序时,在特定的情况下,用raise语句引发自定义异常,然后再捕获。这样就可以更好的知晓程序运行情况并且可以做相应的异常处理。
因为错误是在解释时没有通过解释器,因此没有进入到程序执行的阶段,所以错误是无法被捕获的。如下:
try:
a = 1/0
print('我是一只小小鸟)
except SyntaxError:
print(1)
except ZeroDivisionError:
print(2)
File "E:\BaiduNetdiskWorkspace\FrbPythonFiles\study\test1.py", line 3 print('我是一只小小鸟) ^
SyntaxError: unterminated string literal (detected at line 3)
这个例子可以很好地区分错误和异常。我们看到结果是报:SyntaxError错误,这是因为代码先进行解释,然后发现了SyntaxError错误。这就说明这段代码没有执行,否则应该会先捕获到ZeroDivisionError错误。