python学习笔记之异常处理
异常处理
错误与异常
错误:通常指程序运行中不可恢复的问题
异常:通常指可以在程序运行时恢复的问题
异常处理的一般语法:
1 | try: (1) |
- 异常处理以
try
开始 - 当异常发生时按照一定的规则执行
except
块,可以存在多个except
块 - 可选的
finally
块,无论如何都会被执行,通常用于清理工作
以一个例子来说明,如下:
1 | 3/0 |
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-3-a0641230c7a8> in <module>()
----> 1 3/0
ZeroDivisionError: division by zero
显然,数字0是不能做除数的,这里会抛出ZeroDivisionError
错误,程序执行被中止。如果我们想执行3/0
时不抛出这样的异常,而是可以人为处理,这时就需要做异常处理了,如下:
1 | try: |
start
division by zero
finally
上边代码中人为加入了两个print
语句是为了看清代码的执行流程。解释器当执行到3/0
时,会抛出上边的ZeroDivisionError
异常,此时解释器就不会在执行try
语句中的print('end')
语句了,而是被except
语句捕捉到,执行此代码块中的语句print(e)
,这里的e
是一个变量,表示把异常的信息保存到这个变量上,这是一个可选择参数,如果没有as e
,那异常信息就不会被保存。最后再执行了finally
代码块中的语句。
except
代码是一个类型匹配的过程,可以允许多次出现,但要注意匹配的顺序,如下:
1 | try: |
Exception
3/0
不是会抛出ZeroDivisionError
的异常吗?这里怎么没有被捕捉到,而是被except Exception:
语句捕捉到了?这是因为异常在python也是一个class,决大多数的异常都是在Exception
类中的,ZeroDivisionError
是Exception
的子类,所以在实际编码中,当有多个except
语句时应该把子类放在前面,父类写在后边。
如果except
后边不接任何类名,表示可以捕捉任何的异常信息。
再来看一下finally
语句,它在异常处理中不管是否捕捉到异常,finally
语句都会被执行,以下边的例子说明:
1 | def p(): |
call p function
finally...
在main
函数中有一个return
语句,执照常理来说,在一个函数中执行return
语句时,此函数就执行结束了,但这里为什么还会执行finally
语句呢?
执行流程大致是这样的:
当解释器执行到return p()
语句时,先调用p
函数,输出了call p function
,接着并没有把结果return回去,而是把此时的状态保存起来后去执行finally
语句,执行完成后把之前保存的状态恢复后再执行return
操作,这样就成上边调用main
函数的输出信息了。
抛出异常
抛出异常使用raise
语句
1 | def fn(i): |
---------------------------------------------------------------------------
Exception Traceback (most recent call last)
<ipython-input-3-87e48ca0d898> in <module>()
3 raise Exception('i < 0')
4
----> 5 fn(-4)
<ipython-input-3-87e48ca0d898> in fn(i)
1 def fn(i):
2 if i < 0:
----> 3 raise Exception('i < 0')
4
5 fn(-4)
Exception: i < 0
raise Exception('i < 0')
语句就是抛出一个自定义异常信息,Exception
是类,其实质是抛出了Exception
类的一个实例。
未处理异常
异常未处理时会往上层抛出,如果都没有异常处理时会交给python解释器处理,如下:
1 | def fn(): |
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-5-57d9f8a0a6b1> in <module>()
5 fn()
6
----> 7 main()
<ipython-input-5-57d9f8a0a6b1> in main()
3
4 def main():
----> 5 fn()
6
7 main()
<ipython-input-5-57d9f8a0a6b1> in fn()
1 def fn():
----> 2 3/0
3
4 def main():
5 fn()
ZeroDivisionError: division by zero
上边的异常信息就一层层的往上层抛出,最后由解释器处理了。如果要在main()
捕捉异常呢,如下:
1 | try: |
division by zero
自定义异常
自定义异常:当一个类继承自Exception类或其派生类时。
1 | class MyException(Exception): |
1 | raise MyException('exception') |
---------------------------------------------------------------------------
MyException Traceback (most recent call last)
<ipython-input-8-c6b10bf8a778> in <module>()
----> 1 raise MyException('exception')
MyException: exception
MyException
类继承自Exception
类,所以MyException
类是一个自定义异常类,当执行raise MyException('exception')
语句时抛出的异常就是自定义的异常类。
捕获自定义异常,如下:
1 | try: |
exception