[要点抢先看]
1. 异常的默认处理和主动捕获
2. 主动触发异常和自定义异常
3.finally 终止代码块的用法
[妹子问] 从字面上来看, 异常是程序运行时出现的错误吧.
没错, 每当在运行时检测到程序错误时, python 就会引发异常. 对待异常有两种方法: 一是可以在程序中捕捉和响应错误; 或者忽略已发生的异常.
如果是忽略已发生的异常, python 默认的异常处理行为将启动: 停止程序, 打印出错消息. 如果不想启动这种默认行为, 就要写 try 语句来捕捉异常并从异常中恢复, 当程序运行检测到错误时, python 会跳到 try 处理器, 而程序在 try 之后会重新继续执行.
首先来看看 python 自带的默认异常处理器
- def fetcher(obj, index):
- return obj[index]
- x = 'spam'
- print(fetcher(x,3))
- print(fetcher(x,9))
- m
- Traceback (most recent call last):
- File "E:/12homework/12homework.py", line 7, in <module>
- print(fetcher(x,9))
- File "E:/12homework/12homework.py", line 2, in fetcher
- return obj[index]
- IndexError: string index out of range
复制代码
从这个例子可以看到, 我们试图对字符串末尾以后的位置做索引运算, 当函数尝试执行 obj[9]时, 就会触发异常. Python 会替序列检测到超出边界的索引运算, 并通过抛出 (触发) 内置的 IndexError 异常进行报告.
在这个例子中, 我们的代码没有刻意去捕捉这个异常, 所以他会一直向上返回到程序顶层, 并启用默认的异常处理器: 就是打印标准出错信息, 即异常发生时激活的程序行和函数清单.
那么, 如果我们想自己去捕获异常呢?
因为在有些情况下, 这并不是我们想要的. 例如, 服务器程序一般需要在内部发生错误时依然保持继续工作. 如果你不想要默认的异常行为, 就需要把调用封装在 try 语句内, 自行捕捉异常.
- def fetcher(obj, index):
- return obj[index]
- x = 'spam'
- try:
- fetcher(x,9)
- except IndexError:
- print('got exception')
- got exception
复制代码
现在, 当 try 代码块内程序执行触发异常时, python 会自动跳至处理器 (即 except 分句下面的代码块) 去运行.
- def fetcher(obj, index):
- return obj[index]
- x = 'spam'
- try:
- fetcher(x,9)
- except IndexError:
- print('got exception')
- print('continue...')
- got exception
- continue...
复制代码
在这个例子中, 我们在异常捕捉和处理后, 程序在捕捉了整个 try 语句后继续执行; 这就是我们之所以得到 continue 消息的原因. 我们没有看见标准出错信息, 而程序也将正常执行下去.
除了 python 自身会产生异常以外, 我们在程序中也可以主动引发异常. 想要手动触发异常, 可以直接执行 raise 语句. 用户通过 raise 触发的异常的捕捉方式和 python 程序自身引发的异常一样:
- try:
- raise IndexError
- except IndexError:
- print('got exception')
- got exception
复制代码
如果没有去捕捉到异常, 用户定义的异常就会向上传递, 直到顶层默认的异常处理器, 并通过标准出错信息终止该程序, 看看, 是不是感觉很熟悉.
- raise IndexError
- Traceback (most recent call last):
- File "E:/12homework/12homework.py", line 1, in <module>
- raise IndexError
- IndexError
复制代码
我们还可以自定义异常
刚才我们利用 raise 语句触发了 python 内置作用域中定义的一个内置异常. 其实我们也可以自己定义一个新的异常, 这里可能需要一点面向对象的知识, 所以我们只需要了解即可: 自定义的异常能够通过类来编写, 它继承自一个内置的异常类: 通常这个类的名称叫做 Exception
- class Bad(Exception):
- pass
- def doomed():
- raise Bad()
- try:
- doomed()
- except Bad:
- print('got Bad')
- got Bad
复制代码
最后说说终止行为 finally 代码块
try 语句可以包含 finally 代码块. 可以定义一定会在最后执行时的收尾行为. 这里的 "一定" 指的是无论 try 代码块中是否发生了异常都会执行.
- try:
- raise IndexError
- finally:
- print('in finally')
- print('after finally')
- in finally
- Traceback (most recent call last):
- File "E:/12homework/12homework.py", line 2, in <module>
- raise IndexError
- IndexError
- try:
- print('ok')
- finally:
- print('in finally')
- print('after finally')
- ok
- in finally
- after finally
复制代码
可以看出, 上述 try/finally 语句组合, 无论 try 代码块是否发生异常, 程序都将会执行 finally 代码块中的语句. 但是当有异常发生时, python 会跳过去执行 finally 中的行为, 执行完 finally 中的语句后, 再将 try 中的异常传递给顶层的默认处理器, 因此 finally 后面的语句就不会执行了. 但是如果 try 中的代码不触发异常, 则 finally 后面的代码块就会正常的继续执行.
我们总结一下:
在实际应用中, try/except 的组合可用于捕捉异常并从中恢复, 而 try/finally 的组合则很方便, 可以确保无论 try 代码块内的代码是否发生了异常, 终止行为都一定会运行.
一个例子是: 比如无论是否出现异常, 无论异常是否被捕获, 都一定会确保关闭文件.
最终我们是可以把 try/except/finally 三者连用的, try 内为主体功能代码, except 用来捕获异常, 而无论异常是否出现, 是否被 except 捕获, 都将执行 finally 内的语句.
来源: https://juejin.im/post/5b7d6cd66fb9a019e9767855