每个异常都是某个类的实例。
一些内置的异常类
类名 | 描述 |
---|---|
Exception | 几乎所有的异常类都是从它派生而来的 |
AttributeError | 引用属性或给它赋值失败时引发 |
OSError | 操作系统不能执行指定的任务时引发,有多个子类 |
IndexError | 使用序列中不存在的索引时引发,为LookupError 的子类 |
KeyError | 使用映射中不存在的键时引发,为LookupError 的子类 |
NameError | 找不到名称时引发 |
SyntaxError | 代码不正确引发 |
TypeError | 将内置操作或函数用于类型不正确的对象时引发 |
ValueError | 将内置操作或函数用于这样的对象时引发:其类型正确但包含的值不合适 |
ZeroDivisionError | 在除法或求模运算的第二个参数为零时引发 |
自定义的异常类务必直接或间接地继承Exception
,这意味着从任何内置异常类派生都可以。
捕获异常
异常从函数向外传播到调用函数的地方。
在except子句中使用不带参数的raise抑制异常
-
使用
raise...from...
语句提供异常上下文 -
可使用None来禁用上下文
要使用一个except子句捕获多个异常,可在一个元组中指定这些异常。
except Exception as e
对异常对象检查,排除SystemExit
、KeyboardInterrupt
。从BaseException派生,属于Exception的超类
while True:
try:
statements
except Exception as e:
statements
else:
break
异常处理
或因为外部原因,或因为内部原因,程序会在某些条件下产生异常或者错误。为了提高系统的健壮性和用户的友好性,需要一定的机制来处理这种情况。
Python中常用的异常处理语法是try
、except
、else
、finally
,它们可以有多种组合,如try-except
、try-except-else
、try-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日)