1. 主页
  2. Python基础到高级
  3. 异常处理

异常处理

什么是异常?

异常就是不正常的,按照设计:程序会按照流程走完,但是这个流程会存在各种意外,每个意外都称之为异常

异常在平时的开发中是不可避免的,并且异常并不能称之为错误

错误是不可以捕获处理的,但是异常是可以捕获处理的

例如当找不到需要操作的文件的时候,那么这个意外就是异常

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:

我们要如何帮助您?

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注