python里的异常是一个对象,和其他的对象一样,没有什么特殊之处,不过异常对象是用来表示程序中的错误的,所以它往往带有一些表示程序的错误信息,python中的异常,有的是解释器在执行指令的时候检查出来而抛出的,有的是程序里异常流程raise出来的,还有是语言语句本身抛出来的,像生成器终止时候抛出来一个StopIteration异常,程序里可以使用try/except/finally语句来捕获处理。
python里的内置异常有一个对象类型的继承关系图,见下方:
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
当一个函数/生成器/协程里抛出一个异常时,如果没有使用try语句捕获它,会导致函数/生成器/协程终止,并且向它的上级调用者抛出这个异常,如果不去捕获异常会沿着调用链的下游到上游的方向依次抛出,与此同时解释器还会封装一个traceback的对象链,以记录异常发生时调用栈的情况,方便开发人员进行代码调试。
traceback对象很简单,它是根据函数/生成器/协程的frame对象生成的,它只有几个属性tb_frame、tb_lasti、tb_lineno和tb_next,tb_frame就是函数/生成器/协程的栈帧对象,tb_lasti和tb_lineno表示函数/生成器/协程的code object的最后执行的字节码index和它在源码中的行数,tb_next则指向下一个traceback对象,它是当前函数/生成器/协程调用的函数/生成器/协程的traceback对象,异常沿着调用的逆方向自下而上抛出的时候,解释器同时完成traceback对象链的建立,traceback模块就是根据这个traceback链打印出异常发生时的代码调用关系的。
traceback对象从何处获得呢? 调用sys.exc_info()可得,下面给出一个简单的例子。
import sys, traceback
try:
1/0
except Exception:
_, _, tb = sys.exc_info()
traceback.print_tb(tb)