异常处理

 

目录

异常的捕获... 1

raise. 3

异常类及继承层次:... 4

except子句:... 6

as子句:... 7

finally子句:... 8

异常的传递:... 10

try嵌套:... 11

 

 

 

Error错误:

逻辑错误,算法写错了,加法写成了减法,最难排查;

笔误,变量名写错了,语法错误;

函数或类使用错误,属逻辑错误;

总之,错误是可以避免的,但常常不能完全避免掉,只能尽量少犯错误;

 

Exception异常:

本意是意外情况;

这有个前提,没有出现上面出的错误,也就是说程序写的没问题,但在某些情况下,会出现一些意外,导致程序无法正常的执行下去;

open()操作一个文件,文件不存在,或创建一个文件时已经存在了;访问一个网络文件,突然断网了;

这些就是异常,是意外的情况,异常不可能避免;

 

ErrorException

在高级编程语言中,都有这两个概念,异常是可以捕获并被处理的,但错误是不可能被捕获的;

写一个健壮的程序,尽可能避免错误,尽可能的捕获、处理各种异常;

尽可能写简单,这样逻辑清晰,逻辑错误相对少,可能运行高效;

 

程序会在异常抛出的地方中断执行,如果不捕获,应提前结束程序;

 

例:

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子句最终都会执行,如dictget()给默认值、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 Exceptionraise 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不作限制,可以长到内存放不下,其它语言中有bigintjavaC++),面试题中有大整型如何处理,答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:   #可写BaseExceptionSystemExit,写其它的类则捕获不到,只能写所属异常的类或其父类

    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

是所有内建的、非系统退出的异常的基类,自定义异常应该继承自它;

SyntaxErrorpy将这种错误也归到Exception下,但这种错误不可捕获

ArithmeticError所有算术计算引发的异常OverflowError溢出,如1byte描述的正整数为255如果放256则溢出,py3 int不作限制,可以长到内存放不下,其它语言中有bigintjavaC++),面试题中有大整型如何处理,答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

用于关闭打开的资源,如fileDBnetwork,不用了就自己清理掉,一般占用的资源会越来越多,尽量自己用完清理掉,用的过程中出现异常,也要清理掉;

 

try不能独立存在,要么加except,要么加finally

 

finally的执行时机,一定在最后执行,如有return则前面try中的return不起作用;

如果tryfinally中都有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   #如果tryfinally中都有return,那么函数的返回值是finally中的returnfinally中若没有return要看try中有returntry中的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且其中有returnbreak语句,则异常就不会继续向外抛出,不要这样用,知道这个知识点就行,这样相当于把异常藏起来,外部无法感知到,以后会出大事;

 

例:

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__keyvalue绑定模块对象,在当前模块的作用域中加了个名称进来,再把这个名称和模块的对象关联所以才能用它;.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'>