一、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
块中
网友评论