一、Python的异常处理机制
Python的异常处理要考虑四种不同的时机,可用try、except、else和finally块来表述。
1. finally块
如果:
既要将异常向上传播
又要在异常发生时执行清理操作
那就可以使用try/finally结构。
这种结构有一项常见的用途,就是确保程序能够可靠地关闭文件句柄(还有另外一种写法,参见第43条)
handle = open("/tmp/random_data.txt") # May raise IOError
try:
data = handle.read() # May raise UnicodeDecodeError
finally:
handle.close()
上面的代码中:
read方法所抛出的异常会向上传播给调用方,而finally块中的handle.close方法则一定能执行
open方法必须放在try块外面,因为如果打开文件时发生异常(例如,由于找不到该文件而抛出IOError),那么程序应该跳过finally块
2. else块
如果try块没有发生异常,那么就执行else块。
有了这种else块,就可以尽量缩减try块内的代码量,使其更加易读。
举例
例如,要从字符串中加载JSON字典数据,然后返回字典里某个键所对应的值
import json
def load_json_key(data, key):
try:
result_dict = json.load(data) # may raise ValueError
except ValueError as e:
raise KeyError from e
else:
return result_dict[key] # may raise KeyError
如果不是有效的JSON格式,那么就用json.loads解码时,会产生ValueError。
如果能够解码,那么else块里的查找语句就会执行,他会根据键来查出相关的值。
查询时若有异常,则该异常会向上传播
这种else子句,会把try/except后面的内容和except块本身区分开,使异常的传播行为变得更加清晰。
3. 混合使用
如果要在符合语句中把上面几种机制都用到,那就编写完整的try/except/else/finally结构。
举例
例如,要从文件中读取某项事物的描述信息,处理该事物,然后就地更新该文件。
UNDEFINED = object()
def divide_json(path):
handle = open(path, "r+") # May raise IOError
try:
data = handle.read() # May raise UnicodeDecodeError
op = json.loads(data) # May raise ValueError
value = (op["numerator"] / op["denominator"]) # May raise ZeroDivisionError
except ZeroDivisionError as e:
return UNDEFINED
else:
op["result"] = value
result = json.dumps(op)
handle.seek(0)
handle.write(result) # May raise IOError
return value
finally:
handle.close() # Always Run
二、总结
无论try块是否发生异常,都可利用try/finally复合语句中的finally块来执行清理工作
else块可以用来缩减try块中的代码量,并没有把发生异常时所要执行的语句与try/except代码块隔开
顺利运行try块后,若想使某些操作能在finally块的清理代码之前执行操作,则可将这些操作写到else块中