5.异常处理

作者: Stone_説 | 来源:发表于2021-01-08 20:30 被阅读0次

    目录:
    1.介绍
    2.异常类及继承
    3.as、raise、finally介绍
    4.异常的传递
    5.异常的捕获时机
    6.总结

    1.介绍

    1.1 异常和错误

    异常本意就是意外情况,程序没有错误,但是在某些情况下会出现一些意外,导致程序无法正常的执行下去。异常是可以被捕获并被处理,但是错误是不能被捕获的。
    例如:
    1.open函数操作一个文件,文件不存在,或者创建一个文件时已经存在。
    2.访问一个网络文件时,突然断网。

    eg:

    异常:
    with open('test.txt') as f:
        pass
    
    Traceback (most recent call last):
      File "C:/Users/dell/PycharmProjects/pythonProject/test.py", line 1, in <module>
        with open('test.txt') as f:
    FileNotFoundError: [Errno 2] No such file or directory: 'test.txt'
    #报了异常FileNotFoundError,打开文件不存在
    
    错误:
    def 0A():
        pass
    
      File "C:/Users/dell/PycharmProjects/pythonProject/test.py", line 1
        def 0A():
            ^
    SyntaxError: invalid syntax
    #函数定义错误,以数字0开头
    

    语法或者逻辑不正确的称为错误。
    在语法逻辑正确,运行时出现的“错误”,称之为异常。

    1.2 产生异常

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

    def foo():
        print('before')
        print(1/0)  # 运行时解释器检测出除零异常
        print('after')
    
    foo()
    
    Traceback (most recent call last):
      File "C:/Users/dell/PycharmProjects/pythonProject/test.py", line 6, in <module>
        foo()
      File "C:/Users/dell/PycharmProjects/pythonProject/test.py", line 3, in foo
        print(1/0)
    ZeroDivisionError: division by zero
    before
    

    raise语句显式的抛出异常

    def foo():
        print('before')
        raise Exception('my exception') # 程序运行时,raise主动抛出异常
        print('after')
    
    foo()
    
    before
    Traceback (most recent call last):
      File "C:/Users/dell/PycharmProjects/pythonProject/test.py", line 6, in <module>
        foo()
      File "C:/Users/dell/PycharmProjects/pythonProject/test.py", line 3, in foo
        raise Exception('my exception')
    Exception: my exception
    
    1.3 异常的捕获
    try:
      逮捕获异常的代码块
    except [异常类型]:
      异常的处理代码块
    
    def foo():
        try:
            print('before')
            c = 1/0
            print('after')  # try语句中,异常之后的语句不会被执行
    
        except:
            print('error')
    
        print('catch the exception')
    
    foo()
    print('****** end ******')
    
    #执行到c = 1/0时,产生异常,使用try...except语句捕获该异常,进而跳转except语句,最后执行try...except之外的语句
    before
    error
    catch the exception
    ****** end ******
    

    捕获指定类型的异常

    def foo():
        try:
            print('before')
            c = 1/0
            print('after')
    
        except ArithmeticError:  # 可以在except子句之后,指定捕获指定的异常类型
            print('error')
    
        print('catch the exception')
    
    foo()
    print('****** end ******')
    

    2.异常类及继承

    2.1 Python中异常的继承关系图
    BaseException
     +-- SystemExit
     +-- KeyboardInterrupt
     +-- GeneratorExit
     +-- Exception
          +-- StopIteration
          +-- StandardError
          |    +-- BufferError
          |    +-- ArithmeticError
          |    |    +-- FloatingPointError
          |    |    +-- OverflowError
          |    |    +-- ZeroDivisionError
          |    +-- AssertionError
          |    +-- AttributeError
          |    +-- EnvironmentError
          |    |    +-- IOError
          |    |    +-- OSError
          |    |         +-- WindowsError (Windows)
          |    |         +-- VMSError (VMS)
          |    +-- EOFError
          |    +-- ImportError
          |    +-- LookupError
          |    |    +-- IndexError
          |    |    +-- KeyError
          |    +-- MemoryError
          |    +-- NameError
          |    |    +-- UnboundLocalError
          |    +-- ReferenceError
          |    +-- RuntimeError
          |    |    +-- NotImplementedError
          |    +-- SyntaxError
          |    |    +-- IndentationError
          |    |         +-- TabError
          |    +-- SystemError
          |    +-- TypeError
          |    +-- ValueError
          |         +-- UnicodeError
          |              +-- UnicodeDecodeError
          |              +-- UnicodeEncodeError
          |              +-- UnicodeTranslateError
          +-- Warning
               +-- DeprecationWarning
               +-- PendingDeprecationWarning
               +-- RuntimeWarning
               +-- SyntaxWarning
               +-- UserWarning
               +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
    
    2.2 BaseException及子类

    BaseException

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

    SystemExit

    # sys.exit()函数引发的异常,异常不捕获处理,就直接交给Python解释器,解释器退出
    import sys
    
    print('before')
    sys.exit(1)
    print('SysExit')
    print('outer')
    
    #程序退出码为1
    before
    Process finished with exit code 1
    
    #捕获异常
    import sys
    
    try:
        sys.exit(1)
    except SystemExit:
        print('SysExit')
    print('outer')
    #  捕获该异常,退出状态码变为0
    SysExit
    outer
    Process finished with exit code 0
    

    KeyboardInterrupt

    # 对应的捕获用户中断行为Ctrl + C
    try:
        import time
        while True:
            time.sleep(3)
            print('******')
    except KeyboardInterrupt:
        print('ctrl + c')
    
    print('****** end ******')
    
    #使用键盘的Ctrl + C则该异常将会被捕获
    (Python_code) C:\Users\dell\PycharmProjects\pythonProject>python test.py
    ******
    ctrl + c
    ****** end ******
    

    Exception及子类

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

    SyntaxError语法错误

    #Python将这种错误也归到异常类下面的Exception下的子类,但是这种错误是不可捕获的
    def foo():
        try:
            0a = 5
        except:
            pass
    
    #执行仍然报语法错误SyntaxError
      File "C:/Users/dell/PycharmProjects/pythonProject/test.py", line 3
        0a = 5
         ^
    SyntaxError: invalid syntax
    

    ArithmeticError

    所有算术计算引发的异常,其子类有除零异常等
    

    LookupError

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

    自定义异常类

    #从Exception继承的类
    class MyException(Exception):
        pass
    
    try:
        raise MyException()
    except MyException:
        print('catch the exception')
    
    #捕获自定义类
    catch the exception
    
    Process finished with exit code 0
    
    2.3 多种异常捕获

    捕获规则:

    1.捕获是从上到下依次捕获,如果匹配,则执行匹配的except语句快
    2.如果被一个except语句捕获,其他except语句就不会再次捕获
    3.如果没有任何一个except语句捕获到,则该异常向外抛出
    
    #自行验证
    import sys
    
    class MyException(Exception):
        pass
    
    try:
        a = 1/0
        raise MyException()
        open('sff')
        sys.exit(1)
    
    except ZeroDivisionError:
        print('zero')
    except ArithmeticError:
        print('Arith')
    except MyException:
        print('catch my exception')
    except Exception:
        print('exception')
    except:
        print('sysexit')
    

    3.as、raise、finally介绍

    eg:

    class A:
        pass
    try:
        raise 1
    except:
        print('catch the exception')
    
    catch the exception
    Process finished with exit code 0
    

    变式1:

    class MyException(Exception):
        def __init__(self,code,message):
            self.code = code
            self.message = message
    
    try:
        raise MyException
    except MyException as e:
        print('catch my exception')
    except:
        print('catch ~~~~')
    
    # 运行时MyException为什么会捕获?
    catch ~~~~
    Process finished with exit code 0
    

    变式2:

    class MyException(Exception):
        def __init__(self,code,message):
            self.code = code
            self.message = message
    
    raise MyException
    
    # 直接将MyException抛出,会是TypeError类型的异常
    Traceback (most recent call last):
      File "C:/Users/dell/PycharmProjects/pythonProject/test.py", line 7, in <module>
        raise MyException
    TypeError: __init__() missing 2 required positional arguments: 'code' and 'message'
    

    变式3:

    class MyException(Exception):
        def __init__(self,code,message):
            self.code = code
            self.message = message
    
    try:
        raise MyException
    except MyException as e:
        print('catch my exception: {} {}'.format(e.code,e.message))
    except Exception as e:
        print('{}'.format(e))
    
    #运行结果
    __init__() missing 2 required positional arguments: 'code' and 'message'
    Process finished with exit code 0
    

    变式4:

    class MyException(Exception):
        def __init__(self,code,message):
            self.code = code
            self.message = message
    
    try:
        raise MyException(200,'stone')
    except MyException as e:
        print('catch my exception: {} {}'.format(e.code,e.message))
    except Exception as e:
        print('{}'.format(e))
    
    #MyException异常捕获
    catch my exception: 200 stone
    Process finished with exit code 0
    

    raise语句:

    1.raise后要求应该是BaseException类的子类或实例,如果是类,将被无参实例化
    2.raise后什么都没有,表示抛出最近一个被激活的异常,如果没有被激活的异常,则抛出异常,不过此种方式很少用。
    
    3.2 finally

    不管是否发生异常,try...finally语句块中,不管是否发生异常,finally中子句都会执行
    eg:

    try:
        f = open('test.txt')
    except FileNotFoundError as e:
        print(e.filename,e.strerror,e.errno)
    finally:
        print('清理工作')
        f.close()
    
    # finally中语句块会被执行
    Traceback (most recent call last):
      File "C:/Users/dell/PycharmProjects/pythonProject/test.py", line 7, in <module>
        f.close()
    NameError: name 'f' is not defined  # 抛出NameError异常
    test.txt No such file or directory 2
    清理工作
    Process finished with exit code 1
    

    变式1:解决f的NameError异常

    f = None
    try:
        f = open('test.txt')
    except Exception as e:
        print('{}'.format(e))
    finally:
        print('清理工作')
        if f:
            f.close()
    
    # 给f赋予None,并在finally语句块中再次判断
    [Errno 2] No such file or directory: 'test.txt'
    清理工作
    Process finished with exit code 0
    

    变式2:

    try:
        f = open('test.txt')
    except Exception as e:
        print('{}'.format(e))
    finally:
        print('清理工作')
        try:
            f.close()
        except Exception as e:
            print(e)
    
    # 在finally语句块中进行再次捕获,代码返回值为0
    [Errno 2] No such file or directory: 'test.txt'
    清理工作
    name 'f' is not defined
    Process finished with exit code 0
    
    3.3 finally执行时机
    def foo():
        try:
            return 3
        finally:
            return 5
            print('finally')
        print('==')
    
    print(foo())
    
    # 执行发现,finally中的return语句也会执行
    

    4.异常的传递

    4.1 异常传递
    def foo1():
        return 1/0   # 1.可在此捕获异常
    
    def foo2():
        print('foo2 start')
        foo1()  # 2.可在此捕获异常
        print('foo2 stop')
    
    foo2()  # 3.可在此捕获异常
    
    # 执行结果如下
    Traceback (most recent call last):
      File "C:/Users/dell/PycharmProjects/pythonProject/test.py", line 9, in <module>
        foo2()
      File "C:/Users/dell/PycharmProjects/pythonProject/test.py", line 6, in foo2
        foo1()
      File "C:/Users/dell/PycharmProjects/pythonProject/test.py", line 2, in foo1
        return 1/0
    ZeroDivisionError: division by zero
    foo2 start
    Process finished with exit code 1
    
    总结:
    1.异常总是向外层抛出,如果外层没有处理这个异常,就会继续向外抛出
    2.如果内层捕获并处理了异常,外部就不能捕获到了
    3.如果到了最外层还是没有被处理,就会中断异常所在的线程的执行。
    
    4.2 try嵌套
    try:
        try:
            ret = 1/0
        except KeyError as e:
            print(e)
        finally:
            print('inner fin')
    
    except:
        print('outer catch')
    
    finally:
        print('outer fin')
    
    # 执行结果
    inner fin
    outer catch
    outer fin
    Process finished with exit code 0
    
    总结:
    1.内部捕获不到异常,会向外层传递异常
    2.如果内层有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
    Process finished with exit code 0
    异常被压制,注意:正常写代码是并不压制异常
    

    5.异常的捕获时机

    5.1 立即捕获

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

    def parse_int(s):
        try:
            return int(s)
        except:
            return 0
    
    print(parse_int('re'))
    
    # 执行结果
    0
    Process finished with exit code 0
    
    5.2 边界捕获

    封装产生了边界
    对于一个模块而言,用户调用时,模块内部不想要捕获异常,应该抛出,让外部调用者自己感知,处理
    else子句

    try:
        ret = 10
    except ArithmeticError as e:
        print(e)
    else:
        print('OK')
    finally:
        print('fin')
    
    # 执行结果如下,没有任何异常发生时,则执行else子句
    OK
    fin
    Process finished with exit code 0
    

    6.总结

    try:
      <语句>    # 运行别的代码
    except <异常类>:
      <语句>    # 捕获某种类型的异常
    except <异常类> as <变量名>:
      <语句>    # 捕获某种类型的异常并获得对象
    else:
      <语句>    # 如果没有异常发生
    finally:
      <语句>    # 退出try时总会执行
    

    try工作原理:

    1.如果try中语句执行时发生异常,搜索except子句,并执行第一个匹配该异常的except子句
    2.如果try中语句执行时发生异常,却没有匹配的except子句,异常将被递交到外层的try,如果外层不处理这个异常,异常将继续向外层传递。
      如果都不处理异常,则会传递到最外层,如果还没有处理,就终止异常所在的线程
    3.如果在try执行时没有发生异常,如有else子句,可执行else子句中的语句
    4.无论try中是否发生异常,finally子句最终都会执行
    

    相关文章

      网友评论

        本文标题:5.异常处理

        本文链接:https://www.haomeiwen.com/subject/ztwsoktx.html