Python 异常处理 详解

  • 1、错误和异常
  • 1.1 错误 `Error`
  • 1.2 异常 `Exception`
  • 1.3 总结
  • 2、产生异常
  • 3、捕获异常
  • 3.1 语法
  • 3.2 示例 1
  • 3.3 示例 2
  • 4、 异常类
  • 4.1 `Exception hierarchy`
  • 4.2 `BaseException`及子类
  • 4.3 `sys.exit()`示例 1
  • 4.4 `sys.exit()`示例 2
  • 4.5 `KeyboardInterrupt`示例
  • 4.6 `SyntaxError`示例
  • 4.7 自定义类示例
  • 5、多种捕获
  • 5.1 捕获规则
  • 5.2 示例 1
  • 5.3 示例 2
  • 6、其它子句介绍
  • 6.1 示例 1
  • 6.2 示例 2
  • 6.3 示例 3
  • 6.4 示例 4
  • 6.5 示例 5
  • 6.6 示例 6
  • 7、异常的传递
  • 7.1 示例 1
  • 7.2 示例 2
  • 8、`try`语句
  • 8.1 `try` 语法
  • 8.2 `try` 工作原理
  • 8.3 示例 1
  • 8.4 示例 2
  • 9、异常的捕获时机
  • 10、自定义异常类注意事项
  • 10.1 异常类写的有问题
  • 10.2 异常类传入参数
  • 10.3 异常类传入默认参数
  • 1、错误和异常

    1.1 错误 Error

  • 逻辑错误:算法写错了,例如加法写成了减法
  • 笔误:例如变量名写错了,语法错误
  • 函数或类使用错误:其实这也属于逻辑错误
  • 总之,错误是可以避免的
  • 1.2 异常 Exception

  • 异常就是意外情况
  • 在没有出现上述错误的前提下,也就是说程序写的没有问题,但是在某些情况下,会出现一些意外,导致程序无法正常执行下去
  • 例如,访问一个网络文件,突然断网了,这就是个异常,是个意外的情况
  • 总之,异常是不可能避免的
  • 1.3 总结

  • 错误和异常:在高级编程语言中,一般都有错误和异常的概念,异常是可以捕获的,并被处理的,但是错误是不能被捕获的
  • 一个健壮的程序,尽可能的避免错误,尽可能的捕获、处理各种异常
  • 2、产生异常

  • 1 raise 语句显式的抛出异常

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

  • 3 程序会在异常抛出的地方中断执行,如果不捕获,就会提前结束程序(其实是终止当前线程的执行)

    def foo():
        print('before')
        print(1/0)  # 捕获异常 
        print('after')
    
    foo()
    # ZeroDivisionError: division by zero
    
    def foo():
        print('before')
        raise Exception('My Exception')  # raise 主动抛出异常 
        print('after')
    
    foo()
    # Exception: My Exception
    
  • 3、捕获异常

    3.1 语法

    try:
        待捕获异常的代码块
    except [异常类型]:
        异常的处理代码块
    

    3.2 示例 1

  • 此例执行到c = 1/0时产生异常并抛出,由于使用了try...except语句块则捕捉到了这个异常

  • 异常生成位置之后语句块将不再执行,转而执行对应的except部分的语句

  • 最后执行try...except语句块之外的语句

    def foo():
        try:
            print('before')
            c = 1/0
            print('after')
        except:
            print('error')
            
        print('catch the exception')
    
    foo()
    print('====== end ======')
    
    before
    error
    catch the exception
    ====== end ======
    
  • 3.3 示例 2

    def foo():
        try:
            print('before')
            print(1/0)
            print('after')
        except ArithmeticError:  # 指定捕获的类型
            print('error')
    
        print('catch the exception')
    
    foo()
    print('====== end ======')
    
    before
    error
    catch the exception
    ====== end ======
    

    4、 异常类

    4.1 Exception hierarchy

    The class hierarchy for built-in exceptions is:
    BaseException
     +-- SystemExit
     +-- KeyboardInterrupt
     +-- GeneratorExit
     +-- Exception
          +-- StopIteration
          +-- StopAsyncIteration
          +-- ArithmeticError
          |    +-- FloatingPointError
          |    +-- OverflowError
          |    +-- ZeroDivisionError
          +-- AssertionError
          +-- AttributeError
          +-- BufferError
          +-- EOFError
          +-- ImportError
          |    +-- ModuleNotFoundError
          +-- LookupError
          |    +-- IndexError
          |    +-- KeyError
          +-- MemoryError
          +-- NameError
          |    +-- UnboundLocalError
          +-- OSError
          |    +-- BlockingIOError
          |    +-- ChildProcessError
          |    +-- ConnectionError
          |    |    +-- BrokenPipeError
          |    |    +-- ConnectionAbortedError
          |    |    +-- ConnectionRefusedError
          |    |    +-- ConnectionResetError
          |    +-- FileExistsError
          |    +-- FileNotFoundError
          |    +-- InterruptedError
          |    +-- IsADirectoryError
          |    +-- NotADirectoryError
          |    +-- PermissionError
          |    +-- ProcessLookupError
          |    +-- TimeoutError
          +-- ReferenceError
          +-- RuntimeError
          |    +-- NotImplementedError
          |    +-- RecursionError
          +-- SyntaxError
          |    +-- IndentationError
          |         +-- TabError
          +-- SystemError
          +-- TypeError
          +-- ValueError
          |    +-- UnicodeError
          |         +-- UnicodeDecodeError
          |         +-- UnicodeEncodeError
          |         +-- UnicodeTranslateError
          +-- Warning
               +-- DeprecationWarning
               +-- PendingDeprecationWarning
               +-- RuntimeWarning
               +-- SyntaxWarning
               +-- UserWarning
               +-- FutureWarning
               +-- ImportWarning
               +-- UnicodeWarning
               +-- BytesWarning
               +-- EncodingWarning
               +-- ResourceWarning
    

    4.2 BaseException及子类

  • BaseException:所有内建异常类的基类是 BaseException
  • SystemExitsys.exit()函数引发的异常,异常不捕获处理,就直接交给Python解释器,解释器退出
  • KeyboardInterrupt:对应的捕获用户中断行为 Ctrl + C
  • Exception及子类:
    1、 Exception是所有内建的、非系统退出的异常的基类,自定义异常类应该继承自它
    2、 SyntaxErrorPython将这种错误也归到异常类下面的Exception下的子类,但是这种错误是不可捕获的
    3、 ArithmeticError:所有算术计算引发的异常,其子类有除零异常等
    4、 LookupError:使用映射的键或序列的索引无效时引发的异常的基类:IndexError, KeyError
    5、 自定义异常类:从Exception继承的类
  • 4.3 sys.exit()示例 1

    import sys
    KeyboardInterrupt
    print('Before')
    sys.exit(1)
    print('SysExit')
    print('outer')
    
    D:\PycharmProjects\pythonProject\202108089\venv\Scripts\python.exe D:/PycharmProjects/pythonProject/202108089/test6.py
    Before
    
    Process finished with exit code 1
    

    4.4 sys.exit()示例 2

    import sys
    
    try:
        sys.exit(1)
    except SystemExit:
        print('SysExit')
    print('outer')
    
    D:\PycharmProjects\pythonProject\202108089\venv\Scripts\python.exe D:/PycharmProjects/pythonProject/202108089/test6.py
    SysExit
    outer
    
    Process finished with exit code 0
    

    4.5 KeyboardInterrupt示例

    try:
        import time
        while True:
            time.sleep(1)
            print('!!!')
    except KeyboardInterrupt:
        print('Ctrl + C')
    print('=== end ===')
    
    PS D:\PycharmProjects\pythonProject\202108089> python .\test8.py
    !!!
    !!!
    Ctrl + C
    === end ===
    PS D:\PycharmProjects\pythonProject\202108089>
    

    4.6 SyntaxError示例

  • 语法错误直接报错,无法捕捉,解释器连执行都不会去做
  • try:
        0a = 5
    except SyntaxError:  # 无法捕获
        print('after')
    
    SyntaxError: invalid syntax
    
    print('=' * 10)
    def 0a():
        print('begin')
    
    try:
        0a()
    except Exception as e:
        print(e, type(e))
    
    D:\PycharmProjects\pythonProject\202108089\venv\Scripts\python.exe D:/PycharmProjects/pythonProject/202108089/test8.py
      File "D:/PycharmProjects/pythonProject/202108089/test8.py", line 1
        def 0a():
            ^
    SyntaxError: invalid syntax
    
    Process finished with exit code 1
    

    4.7 自定义类示例

    class MyException(Exception):
        pass
    
    try:
        raise MyException
    except MyException:  # 捕捉自定义异常
        print('Catch the exception')
    
    Out:
    Catch the exception
    

    5、多种捕获

    多种捕获:except可以指定捕获的类型,捕获多种异常。

    5.1 捕获规则

  • 1 捕获是从上到下依次比较,如果匹配,则执行匹配的except的语句块
  • 2 如果被一个except语句捕获,其它except语句就不会再次捕获了,谁在前谁捕获
  • 3 如果没有任何一个except语句捕获到这个异常,则该异常向外抛出
  • 4 捕获原则:从小到大,从具体到宽泛
  • 5 编写异常时:越具体的异常,越往上写;宽泛的,往下写;尽量不要压制异常
  • 5.2 示例 1

    import sys
    
    class MyException(Exception):
        pass
    
    try:
        print('before')
        a = 1/0
    #     raise MyException()
    #     open('t')
    #     sys.exit(1)
    
    except MyException:
        print('catch my exception')
    except ArithmeticError:  # 可以捕获除零异常
        print('ari')
    except ZeroDivisionError:  # 可以捕获除零异常
        print('zero')
    except MyException:
        print('catch my exception')
    except BaseException:  # 可以捕获除零异常
        print('Base')
    except Exception:  # 可以捕获除零异常
        print('exception')
    except:
        print('sysexit')
    

    5.3 示例 2

    class A:pass
    
    try: 
        raise A()
    except:
        print('catch the exception')
    
    Out:
    catch the exception
    

    6、其它子句介绍

  • as子句:被抛出的异常,应该时异常类的实例,可以使用as子句获得这个对象
  • raise语句:raise后要求应该是BaseException类的子类或实例,如果是类,将被无参实例化;raise后什么都没有,表示抛出最近一个被激活的异常,如果没有被激活的异常,则抛出类型异常;raise是用raise语句来引发一个异常。异常/错误对象必须有一个名字,且它们应是ErrorException类的子类
  • Python用异常对象(exception object)表示异常情况,遇到错误后,会引发异常
  • 如果异常对象并未被处理或捕捉,程序就会用所谓的回溯(Traceback,一种错误信息)终止执行
  • finally子句:最终,即最后一定要执行的,try...finally语句块中,不管是否发生了异常,都要执行finally的部分
  • else子句:没有任何异常发生,则执行
  • 6.1 示例 1

    class MyException(Exception):
        def __init__(self, code, message):
            self.code = code
            self.message = message
    
    try: 
        raise MyException()
        raise 1/0
        # raise
    except MyException as e:
        print('catch my exception')
    except Exception as e:
        print(e)
        print('=== end ===')
    
    Out:
    __init__() missing 2 required positional arguments: 'code' and 'message'
    === end ===
    

    6.2 示例 2

    class MyException(Exception):
        def __init__(self, code, message):
            self.code = code
            self.message = message
    
    try: 
        # raise MyException()
        raise 1/0
        # raise
    except MyException as e:
        print('catch my exception')
    except Exception as e:
        print(e)
        print('=== end ===')
    
    Out:
    division by zero
    === end ===
    

    6.3 示例 3

    f = None
    try:
        f = open('111.txt')
    except Exception as e:
        print(e.__class__, e.errno, e.strerror)
    finally:
        print('clear working')
        try:
            f.close()
        except Exception as e:
            print(e)
    
    Out:
    <class 'FileNotFoundError'> 2 No such file or directory
    clear working
    'NoneType' object has no attribute 'close'
    

    6.4 示例 4

    # 函数的返回值取决于最后一个执行的return语句,而finally则是try...finally中最后执行的语句块
    
    def foo():
        # return 1
        try:
            return 3
        finally:
            return 5
            print('Finally')
        print('===')
        
    print(foo())
    
    Out:
    5
    

    6.5 示例 5

    try:
        ret = 1/0
        # ret = 1 * 0
    except ArithmeticError as e:
        print(e)
    else:
        print('OK')
    finally:
        print('fin')
    
    division by zero
    fin
    

    6.6 示例 6

    try:
        # ret = 1/0
        ret = 1 * 0
    except ArithmeticError as e:
        print(e)
    else:
        print('OK')
    finally:
        print('fin')
    
    OK
    fin
    

    7、异常的传递

    7.1 示例 1

  • foo2调用了foo1foo1产生的异常,传递到了foo2

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

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

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

  • 注意整个程序结束的状态返回值

    def foo1():
        return 1/0
    
    def foo2():
        print('foo2 start')
        foo1()
        print('foo2 stop')
    
    foo2()
    
    D:\PycharmProjects\pythonProject\202108089\venv\Scripts\python.exe D:/PycharmProjects/pythonProject/202108089/test6.py
    Traceback (most recent call last):
    	... ...
        return 1/0
    ZeroDivisionError: division by zero
    foo2 start
    
    Process finished with exit code 1
    
  • 7.2 示例 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('Everything is OK.')
        print(threading.enumerate())
    

    8、try语句

    8.1 try 语法

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

    8.2 try 工作原理

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

    # try嵌套
    # 内部捕获不到异常,会向外层传递异常
    # 但是如果函数内层有finally且其中有return break语句,则异常就不会继续向外抛出
    try:
        try:
            ret = 1/0
        except KeyError as e:
        # except ZeroDivisionError as e:
            print(e)
        finally:
            print('inner fin')
    except:
        print('outer catch')
    finally:
        print('outer fin')
    
    inner fin
    outer catch
    outer fin
    

    8.4 示例 2

    # try嵌套
    # 内部捕获不到异常,会向外层传递异常
    # 但是如果函数内层有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
    

    9、异常的捕获时机

  • 1 立即捕获:需要立即返回一个明确的结果

  • 2 边界捕获:封装产生了边界

    def parse_int(s):
        try:
            return int(s)
        except:
            return 0
    
    print(parse_int('s'))
    
    Out:
    0
    
  • 10、自定义异常类注意事项

    10.1 异常类写的有问题

    # 自己写的异常类没写对,抛出异常,被exception捕获
    
    class MyException(Exception):
        def __init__(self, code, msg):
            self.code = code
            self.msg = msg
        
    try:
        raise MyException  # 抛出的异常都是实例
    except NotImplementedError as e:
        print('not imp', e, type(e))
    except MyException as e:
        print('my exception', e, type(e))
    except Exception as e:
        print('all exception', e, type(e))
        
    print('end')
    
    all exception __init__() missing 2 required positional arguments: 'code' and 'msg' <class 'TypeError'>
    end
    

    10.2 异常类传入参数

    class MyException(Exception):
        def __init__(self, code, msg):
            self.code = code
            self.msg = msg
        
    try:
        raise MyException(999, '123')
    except NotImplementedError as e:
        print('not imp', e, type(e))
    except MyException as e:
        print('my exception', e, type(e))
    except Exception as e:
        print('all exception', e, type(e))
        
    print('end')
    
    my exception (999, '123') <class '__main__.MyException'>
    end
    

    10.3 异常类传入默认参数

    class MyException(Exception):
        def __init__(self, code="200", msg="Not OK"):
            self.code = code
            self.msg = msg
        
    try:
        raise MyException  # 等效于raise MyException()
    except NotImplementedError as e:
        print('not imp', e, type(e))
    except MyException as e:
        print('my exception', e, type(e), e.code)
    except Exception as e:
        print('all exception', e, type(e))
        
    print('end')
    
    my exception  <class '__main__.MyException'> 200
    end
    

    来源:Lee木木

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python 异常处理 详解

    发表评论