Table of Contents
异常
一:为什么会出现异常
1:造成代码冗余
我们下看下面的代码
>>> def inner(value): #假设inner是python的一个底层函数
if value == 1:
return value
print('do something')
return r
>>> def center(value): #center是python的一个内置函数,可以被我们调用
return inner(value)
>>> def outter(value): #我们在调用的时候阅读了api,知道了,如果返回值为1,就代表报错
if center(value) == 1: #所以我们判断返回值,从而做了不同的处理
print('error')
else:
print('no error')
>>> outter(1)
error
从上面的代码中,可以看出,由于最内部的函数,为了提示给调用者错误信息,导致调用者也需做相应的处理。从而给调用者造成了麻烦,这里的center函数还得将inner函数的结果返回给outter。
2:无法知道详细的错误信息
我们可以看到,上面代码只能知道我们调用outter函数时出现错误了,但是我们并无法定位到导致错误的根源。
二:抛出异常
为了解决上面所存在的问题,我们可以在某种情况下选择去抛出一个异常,然后在最外层调用的地方直接对异常做处理就好了,而中间涉及的那些函数也无需将内部函数的处理结果返回。
1:异常的本质
异常实际上就是一个 类,异常类 有很多,所有异常的父类是 BaseException
这里可以看到所有的异常类以及继承关系。
2:自定义异常
我们也可以自己定义一个异常类,然后通过raise关键字抛出。
如下:
class DatabaseException(Exception): #自定义的异常类一般都继承Exception
def __init__(self,err='数据库错误'): #err是自定义的异常报错的信息
Exception.__init__(self,err) #调用Exception的初始化对象的方法,把err传进去
测试:
>>> def testRaise():
raise DatabaseException()
print('1111')
>>> testRaise()
Traceback (most recent call last): #告诉我们下面是错误信息的描述
File "<pyshell#154>", line 1, in <module> #代码的第一行 调用testRaise函数时出错
testRaise()
File "<pyshell#150>", line 2, in testRaise #testRaise函数内部第二行出错
raise DatabaseException() #在执行这行时抛出了异常
DatabaseException: 数据库错误
我们可以看到 1111 并没有输出,由于系统发现了异常程序就会退出,不会再运行下面的代码。
但有时候我们仅仅是想将错误信息提示出来,但代码还可以继续执行,这时候我们需要将异常捕获。
三:捕获异常
1:作用
当系统发现了异常,我们将其捕获,然后可以做相应的处理,且程序不会中断
2:使用
>>> class DatabaseException(Exception):
def __init__(self,err='数据库错误'):
Exception.__init__(self,err)
>>> def t():
try: #我们将可能会报错的代码放入到try代码块中
testRaise()
print('try') #报错以后,try代码块中后面的代码不会再继续执行
except DatabaseException as e:
print(e) #输出错误信息
finally:
print('finally') # try代码报错进入到except模块,except执行完会执行finally模块中的代码
print('out of try') #try、except、finally 外部的代码部分也会被执行
>>> t()
数据库错误
finally
out of try
我们会发现,这里出现的错误信息,仅仅是简单的 五个字 "数据库错误" ,这导致我们无法定位到具体出现问题的地方,
我们可以使用python内置的logging模块来显示出我们需要的信息
>>> import logging #导入模块
>>> def t():
try:
testRaise()
print('try')
except DatabaseException as e:
logging.exception(e) #这里做了改动
finally:
print('finally')
print('out of try')
>>> t()
ERROR:root:数据库错误
Traceback (most recent call last):
File "<pyshell#177>", line 3, in t
File "<pyshell#156>", line 2, in testRaise
DatabaseException: 数据库错误
finally
out of try
3:向上抛出异常
先说一下情景以便理解:
首先一个程序员发现数据库被删除了于是他向上级raise情况。
他的上级了解到了情况以后,发现这事大了,他做不了主,得向Ceo去汇报了,即将员工汇报的情况再叙述给Ceo
Ceo了解到了情况做出了决定
>>> class DatabaseException(Exception):
def __init__(self,err='数据库被删了!'):
Exception.__init__(self,err)
>>> def Employee():
raise DatabaseException()
>>> def Boss():
try:
Employee()
except DatabaseException as e:
print(e)
raise #向上抛出捕获到的异常
>>> def Ceo():
try:
Boss()
except DatabaseException as e:
print('收到Boss汇报的信息:',e)
finally:
print('找到谁删的数据库',"开除并将其告到法院!")
>>> Ceo()
数据库被删了!
收到Boss汇报的信息: 数据库被删了!
找到谁删的数据库 开除并将其告到法院!
四:python中内置异常的使用
>>> def T():
try:
print('try...')
r = 10 / 0
print('result:',r)
except Exception as e: #其实我们这里可以明确出现的错误是被除数为0,而这个错误有一个特定的异常类:ZeroDivisionError
logging.exception(e) #所以可以将Exception替换为ZeroDivisionError,使代码更加清晰
finally:
print('finally...')
print('END')
>>> T()
try...
ERROR:root:division by zero
Traceback (most recent call last):
File "<pyshell#220>", line 4, in T
ZeroDivisionError: division by zero
finally...
END
我们还可以使用多个 except模块来分情况捕获不同的错误从而有不同的对应策略,也可以添加一个else模块代表如果没有捕获到异常执行某些代码。
try:
print('try...')
r = 10 / int('2')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
else:
print('no error!')
finally:
print('finally...')
print('END')
五:使用except注意事项
如果有多个except模块,且捕获的异常存在父子关系,那么子异常一定要在父异常前面捕获
try:
foo()
except ValueError as e:
print('ValueError')
except UnicodeError as e:
print('UnicodeError')
第二个except模块永远也捕获不到异常,因为 ValueError是UnicodeError的父类,父类可以捕获到子类的异常。如果出现了异常会全部被第一个except捕获。