什么是异常?
异常就是不正常的,按照设计:程序会按照流程走完,但是这个流程会存在各种意外,每个意外都称之为异常
异常在平时的开发中是不可避免的,并且异常并不能称之为错误
错误是不可以捕获处理的,但是异常是可以捕获处理的
例如当找不到需要操作的文件的时候,那么这个意外就是异常
In [1]: open('./test.txt', 'r')
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
<ipython-input-1-eaebb61ac0f6> in <module>()
----> 1 open('./test.txt', 'r')
FileNotFoundError: [Errno 2] No such file or directory: './test.txt'
出现解释器无法解析的指令的时候,那么这个意外就是错误,这样的错误我们是无法捕获处理的
In [2]: defs s():
File "<ipython-input-2-977d5b77d9a3>", line 1
defs s():
^
SyntaxError: invalid syntax
抛出异常
异常是不可能凭空出现的,肯定是程序抛出的
python中使用raise抛出一个异常,当异常抛出的时候,程序会提前返回
In [3]: def Test():
...: print('start')
...: raise TypeError('int') # 抛出异常
...: print('end')
...:
In [4]: Test()
start
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-d1e5fd9e553c> in <module>()
----> 1 Test()
<ipython-input-3-e075ad63a3dd> in Test()
1 def Test():
2 print('start')
----> 3 raise TypeError('int')
4 print('end')
5
TypeError: int
捕获异常
在所有的高级编程语言中都是存在一种try…except…finally…的错误处理机制的,python也不例外。
在python中异常处理的代码格式是这样的
try:
正常执行的代码块
except:
当正常执行的代码块出现异常的时候执行
finally:
不管是正常执行,还是出现异常执行,都会执行当前代码块的内容,因此 finally 是可有可无的
举一个没有 finally的例子
In [2]: try:
...: raise Exception() # 当正常执行的代码块中出现异常的时候,就会提前返回代码块
...: except: # 默认捕获所有异常
...: print('异常执行') # 当出现异常的时候执行这里
...:
异常执行
在我们日常的开发中经常会遇到各种各样的异常,比较常见的例如有:TypeError, IndexError, ValueError, KeyError等等,其实这些异常都是继承自一个 Exception 的类,通过 __mro__ 来查询父类
In [4]: Exception.__mro__ # 可以看到Exception继承自BaseException,而BaseException继承自object
Out[4]: (Exception, BaseException, object)
In [5]: TypeError.__mro__ # TypeError继承自Exception
Out[5]: (TypeError, Exception, BaseException, object)
In [6]: IndexError.__mro__ # ...
Out[6]: (IndexError, LookupError, Exception, BaseException, object)
In [7]: ValueError.__mro__ # ...
Out[7]: (ValueError, Exception, BaseException, object)
__mro__ 可以看到继承了几层类,其中从左到右第一个是自己本身,第二个是父类,依次类推
并且之前的文章中介绍过,在python3中所有的类都是继承自object类,所以object也可以称之为祖类
既然是捕获异常那么,就可以根据抓取到是什么异常,然后来执行对应的代码块,处理不同的操作
In [9]: try:
...: print('start')
...: raise TypeError() # 抛出一个TypeError的错误
...: print('end')
...: except TypeError: # 捕获TypeError的错误
...: print('TypeError: this is test!')
...:
start
TypeError: this is test!
由此可以看出了,当在try代码块执行过程中出现异常的时候,则中断代码块的执行提前返回
然后进行异常捕获的判断,当异常捕获成功则执行except的代码块
except后面可以跟一个异常类,这时候,它仅仅捕获此类及子类的异常
In [10]: try:
...: print('start')
...: raise TypeError()
...: print('end')
...: except Exception:
...: print('TypeError: this is test!')
...:
start
TypeError: this is test!
In [11]: try:
...: print('start')
...: raise IndexError()
...: print('end')
...: except Exception:
...: print('IndexError: this is test!')
...:
start
IndexError: this is test!
不管抛出TypeError还是IndexError都被成功的捕获到了,因此验证了上面的那句话
当直接捕获Exception的时候,就相当于捕获所有的异常信息
有的时候,在开发过程中,语言内置的异常无法满足我们的需求的时候,我们就需要自己来创建异常,既然所有的异常都是继承自Exception的那么,我们自己来写一个类继承自Exception的话,就是我们自己创建的异常了
In [13]: raise NameError() # 使用raise抛出了异常信息,可以看出正常的执行了异常抛出
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-13-1b8a1121e6f2> in <module>()
----> 1 raise NameError()
NameError:
一个try语句,可以带多个except语句,当程序在当前解释器中出现多个异常的时候,就可以用多个except来进行异常捕获
多个except子句的话,匹配方式是从上到下匹配的,因此越具体的异常类应该越放到前面,越一般的异常类应该越放到后面
In [15]: try:
...: raise TypeError()
...: except TypeError:
...: print('type error')
...: except AttributeError:
...: print('attribute error')
...: except Exception:
...: print('Exception')
...:
type error
当前面捕获的异常类是 Exception 的时候,就不会在继续执行下面的except字句了
In [16]: try:
...: raise TypeError()
...: except Exception:
...: print('exception')
...: except AttributeError:
...: print('attribute error')
...: except TypeError:
...: print('type error')
...:
exception
raise抛出的是异常类的实例,因此except可以使用as的子句,获取异常类的实例
在异常处理的时候,可以给异常处理的代码传递一些信息
# 自定义一个异常类
In [19]: class NameError(Exception):
...: def __init__(self, code, message):
...: self.code = code
...: self.message = message
...:
In [20]: try:
...: raise NameError(500, 'some error') # 给类实例传参
...: except NameError as e:
...: print('code: {}, message: {}'.format(e.code, e.message))
...:
code: 500, message: some error
异常的层级
在python的标准库中,只有四个异常是直接继承自BaseException的,通常来说自定义异常是不会直接继承BaseException的
标准库的其他异常都直接或者间接的继承自Exception,所以自定义异常也应该直接或者间接的继承自Exception
In [21]: Exception.__mro__
Out[21]: (Exception, BaseException, object)
In [22]: KeyboardInterrupt.__mro__ # 当按下Ctrl + C的时候, 就会抛出此异常,通常用于退出程序
Out[22]: (KeyboardInterrupt, BaseException, object)
In [23]: SystemExit.__mro__ # 当调用system.exit的时候会抛出此异常,通常用于退出解释器
Out[23]: (SystemExit, BaseException, object)
In [24]: GeneratorExit.__mro__ # 当生成器退出时,会抛出此异常
Out[24]: (GeneratorExit, BaseException, object)
finally
try语句可以带finally子句,无论是否发生异常,finally子句都会执行的
In [2]: try:
...: f = open('./lanyulei.txt')
...: f.write('lanyulei')
...: except Exception:
...: pass
...: finally:
...: f.close()
...:
In [3]: f.closed
Out[3]: True
即使抛出异常没有被except捕获,finally语句块依然会执行
In [4]: try:
...: f = open('./lanyulei.txt')
...: f.write('lanyulei')
...: finally:
...: f.close()
...:
---------------------------------------------------------------------------
UnsupportedOperation Traceback (most recent call last)
<ipython-input-4-dbb84bd5af62> in <module>()
1 try:
2 f = open('./lanyulei.txt')
----> 3 f.write('lanyulei')
4 finally:
5 f.close()
UnsupportedOperation: not writable
In [5]: f.closed
Out[5]: True
因为finally的特性,所以一般资源清理的工作都交给finally处理
另外,在Python中整个try语句就是一个作用域,因此try子句之间的成员是共享的
finallyh是在函数返回之前执行的
In [6]: def p(x):
...: print(x)
...: return x
...:
In [7]: def fn():
...: try:
...: return p(3)
...: finally:
...: return p(5)
...:
In [8]: fn()
3
5
Out[8]: 5
异常的传递
当抛出异常时,如果没有捕获处理,会继续传递到上层作用域,知道最顶层,如果到最顶层还是没有捕获处理,则中断当前线程
异常时具有可传递性的
In [9]: def f1():
...: raise Exception()
...:
In [10]: def f2():
...: f1()
...:
In [11]: f2()
---------------------------------------------------------------------------
Exception Traceback (most recent call last)
<ipython-input-11-fdec4c1c071f> in <module>()
----> 1 f2()
<ipython-input-10-cccab599e2c1> in f2()
1 def f2():
----> 2 f1()
3
<ipython-input-9-d1259b2ffaca> in f1()
1 def f1():
----> 2 raise Exception()
Exception: