异常
异常的概念
即使语句或表达式在语法上是正确的,但在尝试执行时,它仍可能引发错误,这种在执行时检测到的错误被称为异常。

- 错误信息的最后一行告诉我们程序遇到了什么类型的错误
- 异常有不同的类型,其类型名将会作为错误信息的一部分打印出来,如上述的 TypeError
- 异常类型所有行的剩下部分是根据异常类型及其原因提供的详细信息
如果用户怀有恶意,他会通过 traceback 获悉你不希望他知道的信息。例如,他将知道你的程序文件的名称,还将看到部分不能正确运行的代码,有时,训练有素的攻击者可根据这些信息判断出可对你的代码发起什么样的攻击。
捕获异常
try-except
异常是使用try-except
语句代码块处理的,try
语句的工作原理如下:

首先,执行
try
子句(try
和except
关键字之间的(多行)语句);若上述执行过程中无异常发生,跳过
except
子句,此时已完成了try
语句的执行;若执行
try
子句时发生了异常,则跳过try
子句中剩下的部分,转入except
子句;若发生的异常类型和except
关键字后面的异常匹配,则执行该except
子句,然后继续执行整个try
语句之后的代码;若发生的异常和
except
子句中指定的异常都不匹配,则将其传递到外部的try
语句中;若没有找到处理程序,则它是一个未处理异常,程序将停止并显示异常信息。一个
try
语句可以有多个except
子句,但最多只有一个会被执行;另外,一个except
子句可以将多个异常命名为带括号的元组。使用多个
except
子句时,最后一个except
子句可以省略异常名,以用作通配符,但请谨慎使用,因为这很容易掩盖真正的编程错误!不过,最后的通配
except
子句也可以用于打印错误消息,然后重新引发异常(同样允许调用者处理异常):
使用 try … except …
语句捕获异常的另一个巨大好处,就是可以跨越多层调用。比如函数 main() 调用 foo(), foo() 调用 bar(), 如果在 bar() 中出了错,但只要 main() 捕获到了就可以处理。这意味着不需要在每个可能出错的地方去捕获异常,只要在合适的层次去捕获就可以了,大大减少了 try … except … finally
的麻烦。
else
try ... except
语句有一个可选的 else
子句,在使用时必须放在所有的 except
子句后面,它对于在 try
子句不引发异常时必须执行的代码来说很有用,如:

finally
try 语句还有另一个可选的 finally
子句,用于定义在所有情况下都必须执行的清理操作:
finally 子句总会在离开 try 语句前被执行,无论是否发生了异常;
当在 try 子句中发生了异常且尚未被 except 子句处理(或者它发生在 except 或 else 子句中)时,它将在 finally 子句执行后被重新抛出;
当 try 语句的任何其他子句通过 break, continue 或 return 语句离开时,finally 也会在“离开之前”被执行;
在实际应用程序中,finally 子句对于释放外部资源(如文件或网络连接)非常有用,无论是否成功使用资源。


raise主动抛异常
使用 raise
语句可强制抛出异常:
语法:
raise 异常实例/异常类(派生自Exception的类)
实际上,如果
raise
给出的是一个异常类,它也将通过调用没有参数的构造函数来隐式实例化如果不带表达式,
raise
会重新引发当前作用域内最后一个激活的异常;若当前作用域内没有激活的异常,将会引发RuntimeError
来提示错误。如果你仅需确定是否引发了异常而并不打算处理它,则可以在
except
子句中直接使用raise
关键字来重新引发该异常
异常类
异常体系
Python 的异常也是 class,所有的异常类都继承自 BaseException
类。
由于 Python 的异常也是一个类,故而异常还可以有一些关联值,即异常参数,可以通过类名构造异常实例。而在 Python 中为了方便,异常类都定义了__str__()
方法,使得可以直接打印异常实例的参数而无需引用 self.args

- 如上,
except
子句可以在异常名称后用as
指定一个变量,其是所捕捉到的异常的实例,由此可以方便地在except
子句中做一些和异常相关的处理。 - 实际上,如果异常有参数,它们将作为未处理异常抛出的消息的最后一部分,即前述“详细信息”,而被打印输出。如
ZeroDivisionError: division by zero
中的division by zero
(猜的)。
一些 Python标准异常类型:
异常类型 | 说明 |
---|---|
ZeroDivisionError | division by zero |
FileNotFoundError | No such file or directory: 'alice.txt' |
IndentationError: expect an indented block | 忘记缩进 |
IndentationError: unexpected indent | 不必要的缩进 |

自定义异常
异常应该直接或间接地从
Exception
类派生;自定义异常通常应该保持简单,只提供一些属性,这些属性被用作提取有关错误的信息;
一般而言,异常类的名称应该以"Error"结尾;
在创建可能引发多个不同错误的模块时,通常的做法是为该模块定义的异常创建基类,并为不同错误条件创建特定异常类的子类: