一、异常处理
1、错误Error
- 逻辑错误:算法写错了
- 笔误:变量名写错了,语法错误
- 函数或类的使用错误,其实这也属于逻辑错误,错误是可以避免的
2、异常Exception
- 本意就是意外情况,这有个前提,没有出现上面说的错误,也就是说程序写的没有问题,但是在某些情况下会出现一些意外,导致程序无法正常执行下去
3、错误和异常
- 在高级编程语言中,一般都是有错误和异常的概念,异常时可以捕获,并被处理的,但是错误是不能被捕获的
- 一个健壮的程序因尽可能的避免错误,尽可能的捕获、处理各种异常
二、产生异常
1、产生
- raise语句显式的抛出异常,python解释器自己检测到异常并引发它
def foo(): print('before') def bar(): print(1/0) # 除零异常 bar() print('after') foo() def bar(): print('before') raise Exception('my exextion') print('after') bar() 程序会在异常抛出的地方中断执行,如果不捕获异常,就会提前结束程序
三、异常的捕获
try: 待捕获异常的代码块 except [异常类型] 异常的处理代码块 try: print('begin') c =1/0 print('end') except: print('catch the exception') print('outer') 上例执行到c=1/0时产生异常b并抛出,由于使用了try...except语句块则捕获到了这个异常 异常生成位置之后语句将不再执行,转而执行对应的except部分的语句,最后执行try...except语句块之外的语句
四、异常类及继承层次
1、BaseException及子类
- 所有内建异常类的基类是BaseException
2、SystemExit
- sys.exit()函数引发的异常,异常不捕获处理,就直接交给python解释器,解释器退出
举例1: import sys print('before') sys.exit(1) print('SysExit') print('outer') # 是否执行 举例2: #捕获这个异常 import sys try: sys.exit(1) except SystemExit: print('SysExit') print('outer') 举例3: #对应的捕获用户中断行为Ctrl + c try: import time while True: time.sleep(0.5) pass except KeyboardInterrupt: print('crtl + c') print('outer')
3、Exception及子类
- Exception是所有内建的,非系统退出的异常的基类,自定义异常应该继承自它
举例1: def a(): try: 0a = 5 except: pass 执行报错: File "<ipython-input-36-0e87ebfad4e3>", line 4 0a = 5 ^ SyntaxError: invalid syntax SyntaxError语法错误,python将这种错误也归到异常类下面的Exception下的子类,但是这种错误是不可捕获的 ArithmeticError所有算术计算引发的异常,其子类除零异常等 LookupError:使用映射的键或者序列的索引无效时引发的异常的基类:IndexError,KeyError
4、自定义的异常,从Exception继承的类
class MyException(Exception): pass try: raise MyException() except: #捕获自定义异常 print('cathc the exception')
五、异常的捕获
1、except可以捕获多个异常
举例1: class MyException(Exception): pass try: a = 1/0 raise MyException() #自定义的异常 open('a1.txt') except MyException: #捕获自定义异常 print('cathc the MyException') except ZeroDivisionError: print('1/0') except Exception: print('Exception') 捕获规则:捕获是从上到下依次比较,如果匹配,则执行匹配的except语句块 如果被一个except语句捕获,其他except语句就不会再次捕获了 如果没有任何一个except捕获到这异常,则该异常向外抛出 捕获原则:从小到大,从具体到宽泛
2、as 字句
- 被抛出的异常,应该是异常的实例,如何获取得到这个对象,使用as字句
举例1: class MyException(Exception): def __init__(self, code, message): self.code = code self.message = message try: raise MyException() #raise MyException(200,'ok') #raise后跟类名是无参构造实例,因此需要2个参数 except MyException as e: #捕获自定义异常 print('MyException = {}{}'.format(e.code, e.message)) except Exception as e: print('Exception = {}'.format(e)) 执行输出:Exception = __init__() missing 2 required positional arguments: 'code' and 'message' #执行输出:MyException = 200,ok
3、finally子句
- 即最后一定要执行的语句,try...finally语句块中,不管是否发生了异常,都要执行finally的部分
举例1: f = None try: f = open('test.txt') except FileNotFoundError as e: print(e) print('{},{},{}'.format(e.__class__, e.errno, e.strerror)) finally: print('清理工作') if f: f.close() 1、finally执行时机 测试1: def foo(): try: return 3 finally: print('finally') print('===========') print(foo()) 执行报错: finally 3 进入try,执行return 3,虽然函数要返回,但是finally一定还要执行,所以打印了finally后,函数返回 测试2: def foo(): try: return 3 finally: return 5 print('===========') print(foo()) 进入try,执行return 3,虽然函数要返回,但是finally一定要执行,所以执行return 5, 函数返回,5被压在栈顶,所以返回5,简单说,函数的返回值取决于最后一个执行的return语句 而finally则是try...finally中最后执行的语句块
六、异常的传递
举例1: def foo1(): return 1/0 def foo2(): print('foo2 start') foo1() print('foo2 stop') foo2() foo2调用了foo1,foo1产出的异常,传递到了foo2中 异常总是向外层抛出,如果外层没有处理这个异常,就会继续向外抛出 如果内层捕获并处理了异常,外部就不能捕获到了 如果到了最外层还是没有被处理,就会中断异常所在的线程的执行 举例2: #线程中测试异常 import threading import time def foo1(): return 1/0 def foo2(): time.sleep(3) print('foo2 start') foo1() print('foo2 stop') t = threading.Thread(target=foo2) t.start() while True: time.sleep(1) print('Everthing OK') if t.is_alive(): print('alive') else: print('dead') 2、try的嵌套 举例1: try: try: ret = 1/0 except KeyError as e: print(e) else: print('inner OK') finally: print('inner fin') except: print('outer catch') finally: print('outer fin') 内部捕获不到异常,会向外层传递异常,但是如果内层有finally且其中有return,break语句,则异常就不会继续向外抛出,异常被丢弃 举例2: def foo(): try: ret = 1/0 except KeyError as e: print(e) finally: print('inner fin') return #异常被丢弃 try: foo() except: print('outer catch') finally: print('outer fin')
七、异常的捕获时机
1、立即捕获
需要立即返回一个明确的结果 def parse_int(s): try: return int(s) except: return 0 print(parse_int('s'))
2、边界捕获
- 封装产出了边界
- 例如,写了一个模块,用户调用这个模块的时候捕获异常,模块内部不需要捕获,处理异常一旦内部处理了,外部调用者就无法感知了
- 例如open函数,出现的异常交给调用者处理,文件存在了,就不用再创建了,看是否修改还是删除
- 例如自己写了一个类,使用了open函数,但是出现了异常不知道如何处理,就继续向外层抛出
- 一般来说最外层也是边界,必须处理这个异常了,否则线程退出
3、else子句
没有任何异常发生,则执行 try: ret = 1/0 except ArithemticError as e: print(e) else: print('OK') finally: print('fin') 总结: try: <语句> #运行别的代码 except <异常类>: <语句> #捕获某种类型的异常 except <异常类> as <变量名>: <语句> #捕获某种类型的异常并获得对象 else: <语句> # 如果没有异常发生 finally: <语句> #退出try时总会执行
八、try的工作原理
- 如果try中语句执行时发生异常,搜索except子句,并执行第一个匹配该异常的except子句
- 如果try中语句执行时发生异常,却没有匹配的except子句,异常将被递交到外层的try如果外层不处理这个异常,异常将继续向外层传递,如果都不处理该异常,则会传递到最外层,如果还没有处理,就终止异常所在线程
- 如果在try执行时没有发生异常,将执行else子句中的语句
- 无论try中是否发生异常,finally子句最终都会执行