在try/except/else/finally中受益


Python中,有四种可能的情况你要在产生异常的时候采取行动。这些异常可以被try,except,else,finally块自动的捕获。在复合语句中,每一个块都服务着一个独特的目标,并且它们的组合语句也很有用(详见第51项:又如,“从API中定义一个根异常来隔离调用者”)。

finally

当你想使得异常传播的时候可以使用try/finally,但是你还想在代码出现异常的时候仍然能够整洁的运行。一个通用的使用方式就是在try/finally块中可靠的关闭文件处理句柄(详见第43项:对另一个方法而言“为了可重复的使用try/finally,考虑一下contextlibwith语句)。

# 此处可能引发IO异常
handle = open('/tmp/random_data.txt')
try:
    # 可能引发UnicodeDEcode异常
    data = handle.read()
finally:
    # 总是在try语句结束后关闭文件
    handle.close()

由于读文件而引发的异常总是会被抛到调用方代码处,而为保证finally块中的文件操作能被正确的关闭。你必须在try块之前调用open方法,因为这样如果文件不存在会引发IOError,使得能跳过finally块而不会导致代码本身存在逻辑问题。

else

使用try/except/else块可以使得代码中对哪种一场要被处理,哪种异常要往上抛出的处理变得更加清晰。当try块内的代码没有引发异常的时候,else块就会得到执行。else块可以使得try块中的代码变得更加的简短,并且大大改善代码的可读性。例如:你可能想从一个字符串中加载JSON字典数据,并且返回类似于键值对信息的结果。

def load_json_key(data, key):
    try:
        result_dict = json.loads(data)  # 可能引发ValueError异常
    except ValueError as e:
        raise KeyError from e
    else:
        return result_dict[key]       # 可能引发KeyError

如果字符串中包含的数据不是合法的JSON串数据,解码函数json.loads将会引发一个ValueError异常,然后被except块捕获并且处理。如果解码工作可以正常的得到执行,那么在else块中寻找key对应的值的时候就很有可能引发KeyError异常。然后这个一场将会被上抛给调用该函数的代码,因为它们是try块之外的外部代码。else块的存在可以确保try/except块可以处理,分辨的异常有哪些。这使得异常要抛给谁,变得更加的清晰。

大综合

综合性的使用就是把try/else/except/finally都用上,写到一个复合语句中。例如:你想读取一个描述了工作信息的文件,然后处理,再更新到这个文件中。这里,try块就经常的被用于读取文件和处理文件,except块用于处理从try块中捕获到的预期的异常,而else块则习惯用于更新文件内部的信息并且允许相关的异常往上抛出,最后finally块用于关闭文件等打扫战场性质的工作。

UNDEFINED = object()
def divide_json(path):
    handle = open(path,'r+')    # 可能抛出IOError异常
    try:
        data = handle.read()    # 可能引发UnicodeDecodeError异常
        op = json.loads(data)   # 可能引发ValueError异常
        value = (
            op['numeerator'] /   # 这里是除号!
            op['denominator']   # 可能引发ZeroDivisionError异常
    except ZeroDivisionError as e:
        return UNDEFINED
    else:
        op['result'] = value
        result = json.dumps(op)
        handle.seek(0)
        handle.write(result)    # 可能引发IOError异常
        return value
    finally:
        handle.close()        # 肯定能够被执行成功

这样的结构是非常非常实用而且好用的,因为所有的块都能以直观的方法一起工作。例如: 如果一个在else快中写文件数据的时候发生了,finally块仍然会正确的关闭文件,减少了文件损坏的可能。


备忘录

  • try/finally组合语句可以使得你的代码变得很整洁而无视try块中是否发生异常。
  • else块可以最大限度的减少try块中的代码的长度,并且可以可视化地辨别try/except成功运行的部分。
  • else块经常会被用于在try块成功运行后添加额外的行为,但是要确保代码会在finally块之前得到运行。

results matching ""

    No results matching ""