zoukankan      html  css  js  c++  java
  • python 异常处理

    异常Exception

    错误,error

    逻辑错误:算法写错了,加法写成了减法。

    笔误:变量名写错了,语法错误。

    函数或者类使用错误,其实这也是属于逻辑错误。

    总之,错误是可避免的。却不能完全避免。

    异常(exception)

    本意就是意外情况

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

    例如open函数操作一个文件,文件不存在,或者创建了一个文件时,文件已经存在了,或者访问一个网络文件,突然断网了,这就是异常,是个意外的情况。

    异常也是不可避免的。

    错误和异常

    在高级编程语言中,一般都会有错误和异常的概念,异常是可以捕获,并被处理的,但是错误是不可以被捕获的。

    举例

    对比异常和错误

    #异常
    with open("testabc") as f:
        pass
    
    结果为:
    ---------------------------------------------------------------------------
    FileNotFoundError                         Traceback (most recent call last)
    <ipython-input-2-10eb3cc9145e> in <module>
    ----> 1 with open("testabc") as f:
          2     pass
    
    FileNotFoundError: [Errno 2] No such file or directory: 'testabc'
    
    
    #错误
    def 0A():
        pass
    
    结果为:
     File "<ipython-input-4-e8924206c418>", line 2
        def 0A():
            ^
    SyntaxError: invalid syntax

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

    try:
        f = open("testwe.txt")
        print("after")
    except:
        print("exception")
    
    结果为:
    exception

    上面的例子,打开文件产生了异常,所以后面的没有再执行,而执行的except语句块。所以try是捕获异常的,如果没有捕获到异常except就不会执行。

    try:
        f = open("testwe.txt","w")
        print("after")
    except:
        print("exception")
    
    结果为:
    after

    产生异常

    产生:raise语句显示的抛出异常

    Python解释器自己监测到异常并引发它。

    def foo():
        print("before")
        def bar():
            print(1/0)#除0异常
            
        bar()
        print("after")
    foo()
    
    def bar():
        print("before")
        raise Exception("my exception")
        print(after)
        
    bar()
    
    结果为:
    before
    ---------------------------------------------------------------------------
    ZeroDivisionError                         Traceback (most recent call last)
    <ipython-input-6-b875e3e692f5> in <module>
          6     bar()
          7     print("after")
    ----> 8 foo()
          9 
         10 def bar():
    
    <ipython-input-6-b875e3e692f5> in foo()
          4         print(1/0)#除0异常
          5 
    ----> 6     bar()
          7     print("after")
          8 foo()
    
    <ipython-input-6-b875e3e692f5> in bar()
          2     print("before")
          3     def bar():
    ----> 4         print(1/0)#除0异常
          5 
          6     bar()
    
    ZeroDivisionError: division by zero
    
    
    def bar():
        print("before")
        raise Exception("my exception")
        print(after)
        
    bar()
    
    结果为:
    before
    ---------------------------------------------------------------------------
    Exception                                 Traceback (most recent call last)
    <ipython-input-8-823557ea8ce0> in <module>
          4     print(after)
          5 
    ----> 6 bar()
    
    <ipython-input-8-823557ea8ce0> in bar()
          1 def bar():
          2     print("before")
    ----> 3     raise Exception("my exception")
          4     print(after)
          5 
    
    Exception: my exception

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

    raise语句

    raise后什么都没有,表示抛出最近一个被激活的异常,如果没有被激活的异常,则抛类型异常。这种方式很少见。

    try:
        1/0
    except:
        try:
            raise
        except Exception as e:
            print(e)
    
    结果为:
    division by zero

    上面中首先抛出了一个异常,然后执行except语句块,raise捕获最近的一个异常,然后将这个异常打印出来。

    raise后要求应该是baseexception类的子类或实例,如果是类,将被无参实例化。

    异常的捕获

    try:
        待捕获异常的代码块
    except[异常类型]:#如果没有异常类型,那不管什么异常都会捕获。
        异常的处理代码块
    try:
        print("begin")
        c = 1/0
        print("end")
    except:
        print("catch the exception")
    print("outer")
    
    结果为:
    begin
    catch the exception
    outer

    上例执行到c = 1/0时产生异常并抛异常,由于使用了try……except语句块则捕获到了这个异常,异常生成位置之后语句将不再执行,转而执行对应的except部分的语句,最后执行try……except语句块之外的语句。

    捕获指定类型的异常

    try:
        print("begin")
        c = 1/0
        print("end")
    except ArithmeticError:#如果换成Keyerror则捕获不到异常。
        print("catch the ArithmeticError")
    print("outer")
    
    结果为:
    begin
    catch the ArithmeticError
    outer

    异常类及继承层次

    Python异常的继承

     BaseException及子类

    BaseException

    所有内建异常类的基类是baseexception

    SystemExit

    sys.exit()函数引发的异常,异常不捕获处理,就直接交给Python解释器,解释器退出。

    import sys
    print("before")
    sys.exit(1)
    print("sysexit")
    print("outer")#是否执行?
    
    结果为:
    before
    An exception has occurred, use %tb to see the full traceback.
    
    SystemExit: 1
    
    #捕获这个异常
    import sys
    try:
        sys.exit(1)
    except SystemExit:
        print("sysexit")
    print("outer")
    
    结果为:
    sysexit
    outer

    KeyboardInterreut

    对应的捕获用户中断行为ctrl+c,这个只能在命令窗口里面执行。

    try:
        import time
        while True:
            time.sleep(1)
            pass
    except keyboardInterrupt:
        print("ctrl + c")
    print("outer")

    Exception及子类

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

    SyntaxError语法错误

    Python将这种错误也归到异常类下面的exception下的子类,但是这种错误是不可以捕获的。

    def a():
        try:
            0a = 5
        except:
            pass
    
    结果为:
     File "<ipython-input-8-d4aca2aa43e3>", line 3
        0a = 5
         ^
    SyntaxError: invalid syntax

    ArithmeticErroc所有算术引发的异常,其子类有除0异常等。

    LookupError

    使用映射的键或序列的索引无效时引发的异常的基类:IndexError,KeyError

    自定义异常

    从Exception继承的类

    class MyException(Exception):
        pass
    try:
        raise MyException()
    except MyException:#捕获自定义异常
        print("catch the exception")
    
    结果为:
    catch the exception
    class MyException(Exception):
        pass
    try:
        raise MyException()
    except MyException:#捕获自定义异常
        print(MyException.mro())
    
    结果为:
    [<class '__main__.MyException'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>]

    异常的捕获

    class MyException(Exception):
        pass
    
    try:
        a = 1/0#
        raise MyException()#自定义
        open("a1.txt")#
    except MyException:
        print("catch the exception")
    except ZeroDivisionError:
        print("1/0")
    except Exception:#调整except的顺序
        print("exception")
    
    结果为:
    1/0

    捕获规则

    捕获是从上到下依次比较,如果匹配,则执行匹配的except语句块。

    如果被一个except语句捕获,其他except语句就不会再次执行了。

    如果没有任何一个except语句捕获到这个异常,则该异常向外抛出。

    捕获的原则

    从小到大,从具体到宽泛。也就是顺序应该从小到大。一个好的习惯应该加上一个except:。

    as子句

    被抛出的异常,应该是异常的实例,如何获得这个对象呢?使用as子句

    class MyException(Exception):
        def __init__(self,code,message):
            self.code = code
            self.message = message
            
    try:
        raise MyException
    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'

    raise后跟类名是无参构造实例,因此需要两个参数。

    class MyException(Exception):
        def __init__(self,code,message):
            self.code = code
            self.message = message
            
    try:
        raise MyException(200,"ok")
    except MyException as e:
        print("MyException = {} {}".format(e.code,e.message))
    except Exception as e:
        print("exception = {}".format(e))
    
    结果为:
    MyException = 200 ok

    finally子句

    finally

    最终,即最后一定要执行的,try……finally语句块中,不管是否发生了异常,都要执行finally的部分。

    try:
        f = open("t1est.txt")
    except FileNotFoundError as e:
        print("{}{}{}".format(e.__class__,e.errno,e.strerror))
    finally:
        print("清理工作")
        f.close()
    
    结果为:
    
    <class 'FileNotFoundError'>2No such file or directory
    清理工作
    
     
    ---------------------------------------------------------------------------
    AttributeError                            Traceback (most recent call last)
    <ipython-input-11-f9294b26e624> in <module>
          5 finally:
          6     print("清理工作")
    ----> 7     f.close()
    
    AttributeError: 'NoneType' object has no attribute 'close'
    

    注意上例中的f的作用域,上面打开就抛异常了,还没有赋值,所以会出现问题。解决的办法是在外部定义f。try语句块没有开辟新的作用域。

    finally中一般放置资源的清理,释放工作的语句。

    f = None
    try:
        f = open("t1est.txt")
    except FileNotFoundError as e:
        print("{}".format(e))
    finally:
        print("清理工作")
        if f is not None:
            f.close() 
    
    结果为:
    [Errno 2] No such file or directory: 't1est.txt'
    清理工作

    也可以在finally中再次捕获异常

    try:
        f = open("t1est.txt")
    except Exceptin as e:
        print("{}".format(e))
    finally:
        print("清理工作")
        try:
            f.close()
        except NameError as e:
            print(e)

    finally执行时机

    #测试1
    def foo():
        try:
            return 3
        finally:
            print("finally")
        print("==")
    print(foo())
    
    结果为:
    finally
    3
    
    #测试2
    def foo():
        try:
            return 3
        finally:
            return 5
        print("==")
    print(foo())
    
    结果为;
    5

    测试1

    进入try,执行return3,虽然函数要返回,但是finally一定还要执行,所以打印了finally后,函数返回。

    测试2

    今日try,执行return3,虽然函数要返回,但是finally一定还要执行,所以执行return5,函数返回。5被压在栈顶,所以返回5。简单说,函数的返回值取决于最后一个执行的return语句,而finally则是try……finally中最后执行的语句块。

    异常的传递

    def foo1():
        return 1/0
    def foo2():
        print("foo2 start")
        foo1()
        print("foo2 stop")
        
    foo2()
    
    结果为:
    foo2 start
    ---------------------------------------------------------------------------
    ZeroDivisionError                         Traceback (most recent call last)
    <ipython-input-29-1d986328012a> in <module>
          6     print("foo2 stop")
          7 
    ----> 8 foo2()
    
    <ipython-input-29-1d986328012a> in foo2()
          3 def foo2():
          4     print("foo2 start")
    ----> 5     foo1()
          6     print("foo2 stop")
          7 
    
    <ipython-input-29-1d986328012a> in foo1()
          1 def foo1():
    ----> 2     return 1/0
          3 def foo2():
          4     print("foo2 start")
          5     foo1()
    
    ZeroDivisionError: division by zero

    foo2调用了foo1,foo1产生的异常,传递到了foo2中。

    异常总是向外层抛出,如果外层没有处理这个异常,就会继续向外抛出。

    如果内层捕获并处理了异常,外部就不能捕获到了。

    如果到了最外层还没有被处理,就会中断异常所在的线程的执行。

    #线程中测试异常
    import threading
    import time
    
    def foo1():
        return 1/0
    def foo2():
        time.sleep(3)#3秒后抛出异常
        print("foo2 start")
        foo1()
        print("foo2 stop")
        
    t = threading.Thread(target = foo2)
    t.start()
    
    while True:
        time.sleep(1)
        print("everything ok")
        if t.is_alive():
            print("alive")
        else:
            print("dead")
    import threading
    import time
    
    def foo1():
        try:
            1/0
        finally:
            print("foo1 fin")
    def foo2():
        time.sleep(3)#3秒后抛出异常
        print("foo2 start")
        foo1()
        print("foo2 stop")
        
    try:
        foo2()
    except Exception as e:
        print("outer",e)
    finally:
        print("outer fin")
    print("outer")
    
    结果为:
    foo2 start
    foo1 fin
    outer division by zero
    outer fin
    outer

    try嵌套

    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")
    
    结果为:
    inner fin
    outer catch
    outer fin

    内部捕获不到异常,会向外层传递异常。

    但是如果内层有finally且其中有return、break语句,则异常就不会继续向外抛出。

    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")
    
    结果为:
    inner fin
    outer fin

    异常的捕获时机

    1立即捕获

    需要立即返回一个明确的结果

    def parse_int(s):
        try:
            return int(s)
        except:
            return 0
    print(parse_int("s"))
    
    结果为:
    0

    2边界捕获

    封装产生了边界。

    例如,写了一个模块,用户调用这个模块的时候捕获异常,模块内部不需要捕获,处理异常,一旦内部处理了,外部调用者就无法感知了。

    例如,open函数,出现的异常交给调用者处理,文件存在了,就不用再创建了,看是否修改还是删除。

    例如,自己写了一个类,使用了open函数,但是出现了异常不知道如何处理,就继续向外层抛出,一般来说最外层也是边界,必须处理这个异常了,否则线程退出。

    else子句

    try:
        ret  = 1*0
    except ArithmeticError as e:
        print(e)
    else:
        print("ok")
    finally:
        print("fin")
    
    结果为:
    ok
    fin

    else子句,没有任何异常发生,则执行。

    总结

     try的工作原理

    1如果try中语句执行时发生异常,搜索except子句,并执行第一个匹配该异常的except子句。

    2.如果try中语句执行时发生异常,却没有匹配的except子句,异常将被递交到外层的try,如果外层不处理这个异常,异常将继续向外层传递,如果都不处理该异常,则会传递到最外层,如果还没有处理,就终止异常所在的线程。

    3.如果在try执行时没有发生异常,将执行else子句中的语句。

    4无论try中是否发生异常,finally子句最终都会执行。

  • 相关阅读:
    docker compose 配置 redis cluster jenkins
    Spring Core
    Java Case Interview two
    pytest 生成 allure报告(含4要素的对应版本,兼容)
    python中requests库的post请求 4种类型参数
    接口测试流程
    Docker学习篇 搭建jenkins
    Pytest入门 实例
    python selenium css定位6种
    python selenium select标签的下拉框和非select标签的下拉框
  • 原文地址:https://www.cnblogs.com/xpc51/p/11836577.html
Copyright © 2011-2022 走看看