# 异常层级关系 |
下面的图展示了Python中各种异常类的层级关系,可以发现,他们都是继承自BaseException类。
# 6种常见异常 |
- 除0错误(ZeroDivisionError):除数为0
- 名称错误(NameError):变量使用之前未进行声明或者初始化
- 类型错误(TypeError):某些函数或者方法只适用于特定数据类型,若数据类型操作不当会报此错
- 数值错误(ValueError):输入类型正确的情况下,具体输入数值错误
- 索引错误(IndexError):超出序列长度的索引操作
- 属性错误(AttributeError):方法或者属性不适用于该对象
# 除0错误(ZeroDivisionError)
num = 3
num / 0
# 名称错误(NameError)
variable_a
# 类型错误(TypeError)
num = 3
num + '3'
# 数值错误(TypeError)
float('3')
float('hackdata')
# 索引错误(TypeError)
hack_list = ['h', 'a', 'c', 'k']
hack_list[10]
# 属性错误
tuple_hack = ('h', 'a', 'c')
tuple_hack('k')
为了防止程序因出现异常而停止执行,我们要对可能出现的异常提前预警并设置相应的处理方案,这就是异常的捕获。
# 异常的捕获 |
主要通过try...except...
语句来实现异常捕获。将可能出现错误的代码块放在try中,except则根据多种错误类型设置异常处理方案。以下是例子:
try:
# 正常运行
except Exception1 as e:
# 发生Exception1时的处理方式
except Exception2 as e:
# 发生Exception1时的处理方式
else:
# 正确时候执行
finally:
# 无论对错都执行
# 异常的抛出 |
除了代码出错的时候触发错误,我们也可以主动控制抛出异常,通常使用关键词raise
来实现。
raise ValueError('This is a value error')
# 自定义异常 |
当然,python中内建异常类无法覆盖所有异常情况,我们需要根据可能产生的特定错误自定义异常。自定义异常继承于Exception
类,由它开始扩展。下面是一个简单示例:
class MyError(Exception):
pass
raise MyError(u'something error')
"""
示例:自定义NotIntError,捕获非整型错误
"""
class NotIntError(Exception):
def __init__(self, error):
self.error = error
a = [1, 2, "", 4, 5, "a", ['1', '2', '3']]
for i in range(len(a)):
try:
if type(a[i]) != int:
raise NotIntError("错误")
except NotIntError, e:
print e.error
finally:
print a[i]
一个完整的使用自定义异常捕获的例子
# 第一步:定义异常
class Error(Exception):
pass
class OpenFileError(Error):
pass
class WriteContentError(Error):
pass
def write(content):
if isinstance(content, str):
f = open('file.txt', 'w')
try:
f.write(content)
except Exception:
raise WriteContentError # 抛出写入异常
finally:
f.close()
else:
raise OpenFileError # 抛出打开异常
# 第二步,调用代码
import logging
try:
write(2)
except OpenFileError as e: # 对抛出异常的处理
logging.error(e)
except WriteContentError as e: # 对抛出异常的处理
logging.error(e)
except Error:
logging.error("API Error")
except Excetpion:
logging.error("API Bug")
else:
logging.info("ok")
# 一些经验 |
- 只处理你知道的异常,避免捕获所有异常然后吞掉他们
- 抛出的异常要说明原因,有时候知道异常类型也不知道具体缘由
- 不要使用异常来控制流畅,这样很难维护
- 如果有必要,切记要使用finally来释放资源
关于第3点,我们可以来看一个例子:
# 异常处理破坏了代码逻辑
try:
action_a()
action_b()
action_c()
except ActionExceptin as e:
logging.error(e)
else:
action_d()
# 更优雅的处理方式,将异常代码块抽离到另外一个函数中
def action_executor():
action_a()
action_b()
action_c()
def action():
try:
action_executor()
except ActionExceptin as e:
logging.error(e)
action()
action_d()