异常处理
目录
Error错误:
逻辑错误,算法写错了,加法写成了减法,最难排查;
笔误,变量名写错了,语法错误;
函数或类使用错误,属逻辑错误;
总之,错误是可以避免的,但常常不能完全避免掉,只能尽量少犯错误;
Exception异常:
本意是意外情况;
这有个前提,没有出现上面出的错误,也就是说程序写的没问题,但在某些情况下,会出现一些意外,导致程序无法正常的执行下去;
如open()操作一个文件,文件不存在,或创建一个文件时已经存在了;访问一个网络文件,突然断网了;
这些就是异常,是意外的情况,异常不可能避免;
Error和Exception:
在高级编程语言中,都有这两个概念,异常是可以捕获并被处理的,但错误是不可能被捕获的;
写一个健壮的程序,尽可能避免错误,尽可能的捕获、处理各种异常;
尽可能写简单,这样逻辑清晰,逻辑错误相对少,可能运行高效;
程序会在异常抛出的地方中断执行,如果不捕获,应提前结束程序;
例:
def foo():
print('before')
def bar():
print(1 / 0) #ZeroDivisionError: division by zero,除0异常
bar()
print('after')
foo() #运行期异常
异常的捕获
try:
待捕获异常的代码块
except [异常类型]: #不写异常类型则捕获所有异常,所有异常类型的祖先类是BaseException;写异常类型,则捕获指定类型的异常;可写BaseException或指定的异常类型,写其它的类则捕获不到,只能写所属异常的类或其父类
异常处理代码块
try语句块中的异常,会被抓住,异常语句之后的语句将不会执行,转而执行except块中的语句;
try不能独立存在,要么加except,要么加finally;
try中捕获,except中匹配,最后执行finally,如果finally中有异常,是最新激活的异常;
要在最外层调用时管一下,如
try:
foo2()
except:
pass
finally:
pass
外层不管,则交给解释器,解释器不管直接把服务停掉;
try的工作原理:
1、如果try语句执行时发生异常,搜索except子句,并执行第一个匹配该异常的except子句;
2、如果try语句执行时发生异常,却没有匹配的except子句,异常将被传递到外层的try,如果外层不处理这个异常,异常将继续向外层传递,如果都不处理该异常,则会传递到最外层,如果还没有处理,就终止异常所在的线程;
3、如果在try执行时没有发生异常,将执行else子句中的语句;
4、无论try中是否发生异常,finally子句最终都会执行,如dict中get()给默认值、list中的append()时index超界是尾部追加;
异常处理好,能写出健壮的代码;
try: #运行的代码
<语句>
except <异常类>: #捕获某种类型的异常
<语句>
except <异常类> as <变量名>: #捕获某种类型的异常并获取对象
<语句>
else: #如果没有异常发生执行
<语句>
finally: #退出try时总会执行
<语句>
例:
try:
f = open('test111111') #此句若有异常,try块中这句之后的语句不执行
print('later')
f.close()
# 0a = 6 #SyntaxError,捕获不到,解释器拦截到将错误直接抛出,程序终止,其后语句都不执行
except:
print('exception')
print('outer')
输出:
exception
outer
raise
产生异常;
raise语句,显式地抛出异常;
py解释器自己检测到异常并引发它;
raise后什么都没有,表示抛出最近一个被激活的异常,如果没有被激活的异常,则抛类型异常,很少用;
raise后要求应该是BaseException类的子类或实例,如果是类,将被无参实例化,如raise Exception即raise Exception();
例:
def bar():
print('before')
raise Exception('my exception')
print('after')
bar()
输出:
Traceback (most recent call last):
before
File "/home/python/magedu/projects/cmdb/example_exception1.py", line 23, in <module>
bar()
File "/home/python/magedu/projects/cmdb/example_exception1.py", line 21, in bar
raise Exception('my exception')
Exception: my exception
例:
try:
1/0
print('1/0') #不会执行,上句已出错
except:
try:
raise #等价于raise Exception('my exception')
except Exception as e:
print(e)
输出:
division by zero
异常类及继承层次:
所有内建异常类的基类是BaseException;
BaseException
SystemExit #sys.exit([status code])
KeyboardInterrupt #捕获用户中断行为,ctrl + c
GeneratorExit
Exception #是所有内建的、非系统退出的异常的基类,自定义异常应该继承自它
RuntimeError
RecursionError
MemoryError
NameError
StopIteration
StopAsyncIteration
ArithmeticError #所有算术计算引发的异常
FloatingPointError
OverflowError #溢出,py3 int不作限制,可以长到内存放不下,其它语言中有bigint(java、C++),面试题中有大整型如何处理,答py没有这样的问题
ZeroDivisionError
LoopError
IndexError
KeyError
SyntaxError #特例,虽归在Exception里,但发生机制不在这里面;py将这种错误也归到Exception下,但这种错误不可捕获;0a = 1
OSError
BlockingIOError
ChildProcessError
ConnectionError
BrokenPipeError
ConnectionAbortedError
ConnectionRefusedError
ConnectionReetError
FileExistsError
FileNotFoundError
InterruptedError
IsADirectoryError
NotADirectoryError
PermissionError
ProcessLookupError
TimeoutError
SystemExit:
例:
import sys
print('before')
sys.exit(1) #sys.exit()函数引发的异常,顶层代码中(未用try ... except ...捕获)执行不会显示地报错(异常不捕获处理),就直接交给py解释器,解释器退出;sys.exit()可写状态码,同shell的状态返回码
print('exit')
print('outer')
输出:
before
例:
import sys
try:
sys.exit()
except: #可写BaseException或SystemExit,写其它的类则捕获不到,只能写所属异常的类或其父类
print('catch U')
print('outer')
输出:
catch U
outer
KeyboardInterrupt:
捕获用户中断行为,ctrl + c;
例:
import time
try:
while True:
time.sleep(2)
print('~~~~~~')
except KeyboardInterrupt:
print('catch U')
执行输出:
]$ python example_exception1.py #在命令行中使用,pycharm中使用ctrl + c不管用,是文本编辑框
~~~~~~
~~~~~~
^Ccatch U
Exception:
是所有内建的、非系统退出的异常的基类,自定义异常应该继承自它;
SyntaxError,py将这种错误也归到Exception下,但这种错误不可捕获;
ArithmeticError,所有算术计算引发的异常;OverflowError溢出,如1byte描述的正整数为255如果放256则溢出,py3 int不作限制,可以长到内存放不下,其它语言中有bigint(java、C++),面试题中有大整型如何处理,答py没有这样的问题;
LookupError,使用映射的键或序列的索引无效时引发的异常的基类;
自定义异常,从Exception继承的类;
例:
class MyException(Exception):
pass
try:
# 1/0
# open('test111111') #如有某条语句有异常,会依次匹配except中的语句,执行完except中的语句块后,不再执行其它的except语句,except顺序,把最关心的放到前面,能捕获所有的放到最后
raise MyException()
except ArithmeticError:
print('ArithmeticError')
except MyException:
print('catche U')
print(MyException.mro())
except InterruptedError:
print('InterruptedError')
except Exception:
print('Exception')
except: #建议,最好加上此句,以防万一,放到日志
print('expect')
print('outer')
输出:
catche U
[<class '__main__.MyException'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>]
outer
except子句:
except可以捕获多个异常;
越具体的放在上面,越宽泛的放到后面,最后写上except并加到日志中;
捕获规则:
从上到下依次比较,如果匹配,则执行匹配的except语句块;
如果被一个except语句捕获,其它except语句就不会再次捕获了;
如果没有任何一个except语句捕获到这个异常,则该异常向外抛出,程序就挂了(异常很危险);
捕获的原则:从小到大,从具体到宽泛;
as子句:
被抛出的异常,是异常的实例,如raise MyException('my Error'),使用as子句获得这个对象;
例:
class MyException(Exception):
def __init__(self,xdata = 'You wrong'):
self.xdata = xdata
try:
raise MyException('My Error') #要描述清楚;另raise MyException是无参实例化,except中的e为空,如果是自定义的要用有参
# 1/0
except MyException as e:
print('~~~~~~')
print(e,type(e)) #type(e)等同e.__class__,print()和format()函数默认调用str,其它魔术方法默认调用repr
except Exception as e:
print(e,type(e))
print('outer')
输出:
~~~~~~
My Error <class '__main__.MyException'>
outer
finally子句:
最终,即最后一定要执行的,try ... finally ...语句块中,不管是否发生了异常,都要执行finally的部分;
finally中一般放置资源的清理、释放工作的语句;
对于finally子句来说,上面的except有没有捕获到它不关心,最终都执行finally;
用于关闭打开的资源,如file、DB、network,不用了就自己清理掉,一般占用的资源会越来越多,尽量自己用完清理掉,用的过程中出现异常,也要清理掉;
try不能独立存在,要么加except,要么加finally;
finally的执行时机,一定在最后执行,如有return则前面try中的return不起作用;
如果try和finally中都有return,,那么函数的返回值是finally中的return;
例:
try:
f = open('test2222222')
except FileNotFoundError as e:
print(e,type(e))
except Exception as e:
print(e,e.__class__)
finally:
print('finally')
f.close() #NameError,原因f未被赋值,在open()时就已出错,解决办法如下2种
print('outer')
输出:
[Errno 2] No such file or directory: 'test2222222' <class 'FileNotFoundError'>
finally
Traceback (most recent call last):
File "/home/python/magedu/projects/cmdb/example_exception1.py", line 100, in <module>
f.close()
NameError: name 'f' is not defined
解决1:
#f = None
try:
f = None #此处和try的上一行一样,都是在当前作用域
f = open('test2222222')
except FileNotFoundError as e:
print(e,type(e))
except Exception as e:
print(e,e.__class__)
finally:
print('finally')
if f is not None:
f.close()
print('outer')
输出:
[Errno 2] No such file or directory: 'test2222222' <class 'FileNotFoundError'>
finally
outer
解决2:
try:
f = open('test2222222')
except FileNotFoundError as e:
print(e,type(e))
except Exception as e:
print(e,e.__class__)
finally:
print('finally')
try: #在finally中写try ... except ...经常有,而在finally在写return很少
f.close()
except:
pass
输出:
[Errno 2] No such file or directory: 'test2222222' <class 'FileNotFoundError'>
finally
例:
def foo():
try:
# 1/0
return 123
# except ArithmeticError as e:
# print(e,e.__class__)
except:
pass
finally:
print('finally')
return 456 #如果try和finally中都有return,那么函数的返回值是finally中的return,finally中若没有return要看try中有return则try中的return是函数的返回值
# foo()
print(foo())
print('outer')
输出:
finally
456
outer
异常的传递:
处理异常的过程中,若产生新的异常,继续向外抛;
finally中有异常,继续向外抛,finally管不了异常;
例:
def foo1():
try:
1/0
finally:
print('foo1 fin')
def foo2():
try:
foo1() #foo1()产生的异常会传递到foo2中,异常总是向外抛出;果外层没有处理这个异常则继续向外抛出;如果内层处理了异常,外层就捕获不到了;如果到了最外层还是没有被处理,就会中断异常所在的线程的执行
except ArithmeticError as e:
print('division zero')
finally:
print('foo2 fin')
try: #最外层防线,最好在外层管一下,外层不管,则交给解释器,解释器不管直接把服务停掉
foo2()
except Exception as e:
print(e)
finally:
print('outer fin')
print('outer')
输出:
foo1 fin
division zero
foo2 fin
outer fin
outer
例:
import time
import threading
def foo1():
try:
1/0
finally:
print('foo1 fin')
def foo2():
# try:
# foo1()
# except ArithmeticError as e:
# print('division zero')
# finally:
# print('foo2 fin')
foo1()
# t = threading.Thread(target=foo2)
# t.start()
while True:
time.sleep(1)
print('~~~~~~~~~~~~~~~')
# if t.is_alive():
# print('alive')
# else:
# print('dead')
foo2() #一直往外抛,最外层没人管,该线程退出,该线程若为主线程,进程崩掉,process finished with exit,很严重
try嵌套:
最多3层,再多就要抽象出来;
内部捕获不到异常,会向外层传递异常,但内层有finally且其中有return、break语句,则异常就不会继续向外抛出,不要这样用,知道这个知识点就行,这样相当于把异常藏起来,外部无法感知到,以后会出大事;
例:
import time
def foo():
time.sleep(1)
try:
1/0
finally:
print('foo1 fin')
return #异常将被丢弃
try:
foo()
except Exception as e: #该句不会执行,内层的finally中有return语句
print('outer except',e)
finally:
print('outer fin')
print('outer')
输出:
foo1 fin
outer fin
outer
异常的捕获时机:
1、立即捕获:
需要立即返回一个明确的结果;
例:
def foo():
try:
f = open('test333','r')
except FileNotFoundError: #更细化,立即捕获,立即处理
pass
except FileExistsError: #更细化,立即捕获,立即处理
pass
finally:
try:
f.close()
except:
pass
例:
# def getaint(data):
# try:
# res = int(data)
# except Exception:
# res = 0
# return res
def getaint(data):
try:
return int(data) #技巧
except:
return 0
print(getaint('888'))
print(getaint('aaa'))
输出:
888
0
2、边界捕获:
封装产生了边界;
如,写了一个模块,用户调用这个模块时捕获异常,模块内部不需要捕获、处理异常,一旦内部处理了,外部调用者就无法感知了;
如,open(),出现的异常交给调用者处理,文件存在了,就不要再创建了,看是否修改或是删除;
如,自己写一个类,使用了open(),但出现了异常不知如何处理,就继续向外层抛出,一般来说最外层也是边界,必须处理这个异常了,否则线程退出;
else子句:
try中没有任何异常,执行else子句;
例:
try:
# 1/0
1
except ArithmeticError:
print('ArithmeticError')
except:
pass
else:
print('else')
finally:
print('fin')
例:
try:
import functools
print(dir()) #__dict__的key,value绑定模块对象,在当前模块的作用域中加了个名称进来,再把这个名称和模块的对象关联所以才能用它;.py文件天然就是一个模块,一切都是__dict__
except Exception as e:
print(e)
输出:
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'functools']
例:
import functools
print(dir())
print(dir(functools))
print(functools)
print(functools.wraps) #有内存地址,则被加载初始化过
输出:
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'functools']
['MappingProxyType', 'RLock', 'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES', 'WeakKeyDictionary', '_CacheInfo', '_HashedSeq', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_c3_merge', '_c3_mro', '_compose_mro', '_convert', '_find_impl', '_ge_from_gt', '_ge_from_le', '_ge_from_lt', '_gt_from_ge', '_gt_from_le', '_gt_from_lt', '_le_from_ge', '_le_from_gt', '_le_from_lt', '_lru_cache_wrapper', '_lt_from_ge', '_lt_from_gt', '_lt_from_le', '_make_key', 'cmp_to_key', 'get_cache_token', 'lru_cache', 'namedtuple', 'partial', 'partialmethod', 'recursive_repr', 'reduce', 'singledispatch', 'total_ordering', 'update_wrapper', 'wraps']
<module 'functools' from '/ane/python3.6/lib/python3.6/functools.py'>
<function wraps at 0x7ff1208b4950>
例:
import os.path
print(dir()) #名词空间中仅有'os',访问时仍要os.path这种完全限定名访问,可用as子句帮忙,如import os.path as op
print(os)
print(os.path)
输出:
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'os']
<module 'os' from '/ane/python3.6/lib/python3.6/os.py'>
<module 'posixpath' from '/ane/python3.6/lib/python3.6/posixpath.py'>