Python进阶之路 9.3.6 异常捕捉中的finally子句

9.3.6 异常捕捉中的finally子句

捕捉异常语句的最后一个子句是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

你可能感兴趣的:(Python,Python进阶之路)