捕捉异常语句的最后一个子句是finally。从这个子句的名字基本上可以判定是做什么用的。所有需要最后收尾的代码都要放到finally子句中。不管是正常执行,还是抛出异常,最后都会执行finally子句中代码,所以应该在finally子句中放置关闭资源的代码,如关闭文件、关闭数据库等。
如果使用return语句退出函数,那么首先执行finally自居中的代码,才会退出函数。因此并不用担心finally自居中的代码不会被执行,只要为try语句加上了finally子句,并且程序执行流程进入了try语句,finally自居中的代码是一定会执行的。
try:
...
except:
...
finally: # 无论是否抛出异常,都会执行finally子句中的代码
...
[例 9.7] 本例演示了finally子句在各种场景下的执行情况。
# 未抛出异常时执行finally子句中的代码
def fun1():
try:
print('fun1 正常执行')
finally:
print('fun1 finally')
# 抛出异常时执行finally子句中的代码
def fun2():
try:
raise Exception
except:
print('fun2 抛出异常')
finally:
print('fun2 finally')
# 用return语句退出函数之前执行finally子句中的代码
def fun3():
try:
return 20
finally:
print('fun3 finally')
# 抛出异常时执行finally子句中的代码,但在finally子句中执行del x操作,再一次抛出了异常
def fun4():
try:
x = 1/0
except ZeroDivisionError as e:
print(e)
finally:
print('fun4 finally')
del x
fun1()
fun2()
print(fun3())
fun4()
输出结果:
fun1 正常执行
fun1 finally
fun2 抛出异常
fun2 finally
fun3 finally
20
division by zero
fun4 finally
Traceback (most recent call last):
File "/Users/limingda/PycharmProjects/untitled6/test3.py", line 41, in
fun4()
File "/Users/limingda/PycharmProjects/untitled6/test3.py", line 36, in fun4
del x
UnboundLocalError: local variable 'x' referenced before assignment
从上面的代码可以看到,当在fun3函数中通过return语句退出函数时,会首先执行finally自居中的代码,然后再退出函数。在fun4函数中,尽管finally子句中的代码正常执行了,但在finally子句中试图通过del语句删除x变量,但由于x变量在创建之初由于抛出异常(分母为0),并未创建成功,所以x变量其实并不存在,因此,在使用del语句删除一个并不存在的变量时会抛出异常,而且这次是在finally子句中抛出异常,而且并没有其他try语句捕捉这个异常,所以这个异常将直接导致程序崩溃。因此在finally子句中,应该尽可能避免执行容易抛出异常的语句,如果非要执行这类语句,建议再次加上try语句。
def fun4():
try:
x = 1/0
except ZeroDivisionError as e:
print(e)
finally:
print('fun4 finally')
try:
del x
except Exception as e:
print(e)
输出结果:
local variable 'x' referenced before assignment