2.4Python异常
现在简单介绍Python异常与类的关系,语言内建的异常类层次结构,以及Python语言如何利用面向对象的观点组织异常处理过程。编程中有时需要自己定义异常(类型),如果需要这样做,就应该选一个系统异常类,从它派生。
2.4.1异常类和自定义异常
异常是Python语言中的一套特殊的控制机制,主要用于支持错误的检查和处理,也可以用于实现特殊的控制转移。如果程序执行中发生异常,无论是解释器发现的异常情况(例如除零或类型错误等),还是通过raise语句引发的异常,正常执行控制流立刻终止,解释器转入异常处理模式,查找能处理所发生异常的处理器。如果找不到相应的异常处理器,在交互解释环境下,系统将在环境中输出错误信息,结束当前执行并回到系统的交互状态,等待下一输入。在直接执行方式下,当前程序直接终止。
程序运行中发生的每个异常都有特定的名字,如ValueError、TypeError、ZeroDivision-Error等,解释器根据发生的异常去查找处理器。Python里处理异常的结构是try语句。每个try语句可以带有任意多个except子句,这种子句就是异常处理器,子句头部用一个表达式描述它捕捉和处理的异常。
实际上,Python的异常都是类(class),运行中产生异常就是生成相应类的实例对象,异常处理机制完全基于面向对象的概念和性质。全体内部异常类构成了一个树形结构,所有异常类的基类是BaseException,其最主要的子类是Exception,内置异常类都是这个类的直接或间接派生类。如果用户需要定义异常,就应该从系统异常类中选择一个合适的异常,从它派生出自己的异常类。例如:
class RationalError(ValueError):
pass
最简单的情况(很常见)只是希望定义一种特殊异常,并不需要这种异常有什么特殊功能(如上即是)。在这种情况下,选一个系统异常类派生自己的异常类,类体不需要定义任何属性。但为了语法完整,可以在这里写一个pass语句。
如果运行中发生异常,查找相应处理器的工作由解释器完成,这里的工作方式也基于对象和类的关系。在一个except子句头部可以列出一个或多个异常名(一般说,是表示异常类的表达式),列出多个异常名时需要采用括号括起的元组形式。列在except子句头部的异常名表示本异常处理器准备捕捉和处理的异常。
运行中发生的异常与处理器的匹配按面向对象的方式处理。假设运行中发生的异常是e,如果一个异常处理器头部列有异常名E,且isinstance(e,E)为真,那么这个处理器就能捕捉并处理异常e。举例说,如果运行中引发了一个RationalError异常,某个处理器头部列出了RationalError,或者ValueError,或者Exception,该处理器都能捕捉这个异常。当然,匹配ValueError的处理器还能捕捉和处理其他异常,匹配Exception的处理器能捕捉和处理各种主要异常。
2.4.2异常的传播和捕捉
运行中的异常可能发生在模块层面的语句的执行中,更多情况是发生在某个函数的执行中。假设在函数f的执行中发生异常e,当前执行立即中断,解释器转入异常处理模式,设法找到处理e的处理器。有关查找过程如下:
首先在发生异常的函数体里查找处理器:
如果发生异常的语句位于一个try语句体里,首先顺序检查这个try语句后部的各except子句,检查是否存在能处理e的处理器。
如果发生异常的try语句的所有异常处理器都不能处理e,解释器转去查看包围着该try语句的外围try语句(如果存在),检查是否存在能与e匹配的异常处理器。这个查找过程将在e发生的函数f里逐层进行。
如果e不能在函数f里处理,f的执行异常终止,e在函数f这次执行的调用点重新引发,导致又一轮处理器查找工作。查找规则与上面一样。
如果上面查找过程在某一步找到了与e匹配的处理器,解释器就转去执行该except子句的体(异常处理器代码)。执行完这段代码后,解释器回到正常执行模式,从该异常处理器所在的try语句之后继续执行。
上述查找过程可能导致函数一层层以异常方式退出,有可能一直退到当前模块的最上层也没有找到与之匹配的处理器:
如果程序是在解释器的交互方式下执行,Python解释器终止该模块执行并回到交互状态,输出错误信息后等待用户的下一个命令。
如果程序是自主执行(或称按批处理方式执行),该程序立即终止。
如果异常发生在主模块的表层(不在任何函数里),处理过程同样如上所述。
在异常处理过程中还可能出现一些情况。例如,正在执行处理器代码时又发生了新异常;或者处理中遇到某些特殊情况,需要引发新的异常。Python语言里还有关于这些情况的细节规定,这里就不继续讨论了。
2.4.3内置的标准异常类
Python语言定义了一套标准异常类,它们都是BaseException的派生类,其最重要子类是Exception,标准异常类都是Exception的直接或间接派生类。
下面是一些常见异常,后面有些例子从其中一些异常派生自己的异常类:
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- ArithmeticError
| +-- FloatingPointError
| +-- OverflowError
| +-- ZeroDivisionError
+-- AssertionError
+-- AttributeError
+-- EOFError
+-- ImportError
+-- LookupError
| +-- IndexError
| +-- KeyError
+-- NameError
| +-- UnboundLocalError
+-- OSError
| +-- BlockingIOError
| +-- ChildProcessError
| +-- ConnectionError
| | +-- BrokenPipeError
| | +-- ConnectionAbortedError
| | +-- ConnectionRefusedError
| | +-- ConnectionResetError
| +-- FileExistsError
| +-- FileNotFoundError
| +-- InterruptedError
| +-- IsADirectoryError
| +-- NotADirectoryError
| +-- PermissionError
| +-- ProcessLookupError
| +-- TimeoutError
+-- ReferenceError
+-- RuntimeError
| +-- NotImplementedError
+-- SyntaxError
| +-- IndentationError
| +-- TabError
+-- SystemError
+-- TypeError
+-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
这个图并不完全,还有一些被分类为“警告”(Warning)的异常等。