在处理 Python 异常 时,如果希望代码块不管有没有发生异常,都继续执行某些语句,我们可以使用 finally 子句来实现。
语法:
try:
……
except Exception as reason:
……
finally:
……示例:finally 子句的基本用法
f = None
try:
f = open(r'data\A.txt', 'r', encoding='utf-8')
result = f.read() + 1000
f.close()
except TypeError as reason:
print(reason)运行结果如下。
(报错)TypeError: can only concatenate str (not 'int') to str分析:
在上面示例中,程序从上到下执行,但是执行到 result = f.read() + 1000 这一句代码时就会发生异常,后面的 f.close() 就不会执行了。此时文件已经被打开了,却没有被关闭,这不符合预期。
如果希望不管 try except 代码块中是否发生异常,我们都要执行 f.close() 关闭文件,就可以使用 finally 子句来实现,代码如下:
f = None
try:
f = open(r'data\A.txt', 'r', encoding='utf-8')
result = f.read() + 1000
except TypeError as reason:
print(reason)
finally:
if f: # 只有当f成功被赋值(即文件成功打开)时才尝试关闭
f.close()对于 try...except...finally 语句,如果 try 语句块中没有发生任何异常,则会跳过 except 语句块,然后执行 finally 语句块。如果 try 语句块中发生异常,则会先执行 except 语句块,再执行 finally 语句块。也就是说,finally 语句块是无论是否发生异常,都一定会被执行的内容。异常处理的流程如下图所示。

异常是一个类,捕获异常就是捕获类的实例。前面介绍的都是用于处理 Python 内置的异常,实际上我们还可以自定义异常类。如果要抛出异常,可以根据需要定义一个异常类,然后使用 raise 语句抛出异常类的实例。
需要清楚的是,只有在必要的时候才去自定义一个异常类。平常开发时我们应该尽量使用 Python 内置的异常类(如 NameError、TypeError 等)。因此对于初学者而言,你并不需要过多关注自定义异常类以及 raise 语句。
提示: 如果在实际项目中要忽略某类异常,可以使用标准库模块 contextlib 里的 suppress 函数,它提供了现成的 “忽略异常” 功能。
最后总结一下 Python 异常处理,主要有以下 3 点。
- except 语句可以有多个,Python 会按照顺序执行。如果异常已经被处理,后面的 except 就不会被执行了。
- except 语句可以用元组的形式同时指定多个异常。
- except 语句后面如果不指定异常,则表示捕获所有异常。
finally 子句与 with 语句之间的关系
在前面的章节中,我们强烈推荐使用 with open(...) as f: 来操作文件,这是因为它会自动帮我们关闭文件。
现在小伙伴们学完了 finally 子句,是不是明白了点什么呢?没错!with 语句 的底层原理,其实就是帮我们自动封装了 try...finally 的逻辑!
因此在操作文件时,虽然 finally 能完美胜任,但我们依然更推荐极其简洁的 with 语句。而在释放网络连接、清理数据库连接等其他复杂场景中,finally 才是真正的幕后英雄!
