遵循异常处理的几点基本原则、避免finally中可能发生的陷阱

每个异常都是某个类的实例。

一些内置的异常类

类名描述
Exception几乎所有的异常类都是从它派生而来的
AttributeError引用属性或给它赋值失败时引发
OSError操作系统不能执行指定的任务时引发,有多个子类
IndexError使用序列中不存在的索引时引发,为LookupError的子类
KeyError使用映射中不存在的键时引发,为LookupError的子类
NameError找不到名称时引发
SyntaxError代码不正确引发
TypeError将内置操作或函数用于类型不正确的对象时引发
ValueError将内置操作或函数用于这样的对象时引发:其类型正确但包含的值不合适
ZeroDivisionError在除法或求模运算的第二个参数为零时引发

自定义的异常类务必直接或间接地继承Exception,这意味着从任何内置异常类派生都可以。

捕获异常

异常从函数向外传播到调用函数的地方。

在except子句中使用不带参数的raise抑制异常

  • 使用raise...from...语句提供异常上下文

  • 可使用None来禁用上下文

要使用一个except子句捕获多个异常,可在一个元组中指定这些异常。

except Exception as e对异常对象检查,排除SystemExitKeyboardInterrupt。从BaseException派生,属于Exception的超类

while True:
    try:
        statements
    except Exception as e:
        statements
    else:
        break

异常处理

或因为外部原因,或因为内部原因,程序会在某些条件下产生异常或者错误。为了提高系统的健壮性和用户的友好性,需要一定的机制来处理这种情况。

Python中常用的异常处理语法是tryexceptelsefinally,它们可以有多种组合,如try-excepttry-except-elsetry-finally以及try-except-else-finally等。语法形式如下:

try:
    statements
except name1:   # 当try中发生name1的异常时处理
    statements
except (name2, name3):  # 当try中发生name2或name3的异常时处理
    statements
except name4 as data:   # # 当try中发生name4的异常时处理,并获取对应实例
    statements
except:     # 其他异常发生时处理
    statements
else:   # 没有异常发生时执行
    statements
finally:    # 不管有没有异常发生都会执行
    statements

异常处理通常需要遵循以下几点基本原则:

  • 注意异常的粒度,不推荐在try中放入过多的代码。

  • 谨慎使用单独的except语句处理所有的异常,最好能定位具体的异常。同样也不推荐使用except Exception或者except StandardError来捕获异常。如果在某些情况下不得不使用单独的except语句,最好能够使用raise语句将异常抛出向上层传递。向上层传递的时候需要警惕异常被丢失的情况,可以使用不带参数的raise来传递。

  • 注意异常捕获的顺序,在合适的层次处理异常。为了更精确地定位错误发生的原因,推荐的方法是将继承结构中子类异常在前面的except语句中抛出,而父类异常在后面的except语句抛出。

  • 使用更为友好的异常信息,遵守异常参数的规范。软件最终是为用户服务的,当异常发生的时候,异常信息清晰友好与否直接关系到用户体验。通常来说有两类异常阅读者:使用软件的人和开发软件的人,即用户和开发者。对于用户来说关注更多的是业务。

finally子句

无论try语句中是否有异常抛出,finally语句总会被执行。由于这个特性,finally语句经常被用来做一些清理工作。

但使用finally时,也要特点小心一些陷阱。

# IndexError异常被丢失
def finally_test():
    print("starting...")
    while True:
        try:
            print("running")
            raise IndexError("r")
        except NameError as e:
            print("NameError happened, {excp}".format(excp=e))
        finally:
            print("finally executed")
            break


finally_test()

# starting...
# running
# finally executed

当try块中发生异常的时候,如果在except语句中找不到对应的异常处理,异常将会被临时保存起来,当finally执行完毕的时候,临时保存的异常将会再次被抛出,但如果finally语句中产生了新的异常或者执行了return或者break语句,那么临时保存的异常将会被丢失,从而导致异常屏蔽。

# return语句截断直接返回
def return_test(num):
    try:
        if num <= 0:
            raise ValueError('data cannot be negative')
        else:
            return num
    except ValueError as e:
        print(e)
    finally:
        print("end!")
        return -1


print(return_test(0))
# data cannot be negative
# end!
# -1
print(return_test(2))
# end!
# -1

由于存在finally语句,在执行else语句的return num语句之前会先执行finally中的语句,此时由于finally语句中有return -1,程序就直接返回了,所以永远不会返回num对应的值2。

在实际应用程序开发过程中,并不推荐在finally中使用return语句进行返回,这种处理方式不仅会带来误解而且可能会引起非常严重的错误。

(最近更新:2019年05月16日)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值