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