8.4. try 语句
try
语句可为一组语句指定异常处理器和/或清理代码:
- try_stmt ::=
try1_stmt
|try2_stmt
- try1_stmt ::= "try" ":"
suite
- ("except" [
expression
["as"identifier
]] ":"suite
)+- ["else" ":"
suite
]- ["finally" ":"
suite
]- try2_stmt ::= "try" ":"
suite
- "finally" ":"
suite
except
子句指定一个或多个异常处理器。 当 try
子句中没有发生异常时,没有异常处理器会被执行。 当 try
子句中发生异常时,将启动对异常处理器的搜索。 此搜索会依次检查 except 子句,直至找到与该异常相匹配的子句。 如果存在无表达式的 except 子句,它必须是最后一个;它将匹配任何异常。 对于带有表达式的 except 子句,该表达式会被求值,如果结果对象与发生的异常“兼容”则该子句将匹配该异常。 一个对象如果是异常对象所属的类或基类,或者是包含有兼容该异常的项的元组则两者就是兼容的。
如果没有 except 子句与异常相匹配,则会在周边代码和发起调用栈上继续搜索异常处理器。 1
如果在对 except 子句头中的表达式求值时引发了异常,则原来对处理器的搜索会被取消,并在周边代码和调用栈上启动对新异常的搜索(它会被视作是整个 try
语句所引发的异常)。
当找到一个匹配的 except 子句时,该异常将被赋值给该 except 子句在 as
关键字之后指定的目标,如果存在此关键字的话,并且该 except 子句体将被执行。 所有 except 子句都必须有可执行的子句体。 当到达子句体的末尾时,通常会转向整个 try 语句之后继续执行。 (这意味着如果对于同一异常存在有嵌套的两个处理器,而异常发生于内层处理器的 try 子句中,则外层处理器将不会处理该异常。)
当使用 as
将目标赋值为一个异常时,它将在 except 子句结束时被清除。 这就相当于
- except E as N:
- foo
被转写为
- except E as N:
- try:
- foo
- finally:
- del N
这意味着异常必须赋值给一个不同的名称才能在 except 子句之后引用它。 异常会被清除是因为在附加了回溯信息的情况下,它们会形成堆栈帧的循环引用,使得所有局部变量保持存活直到发生下一次垃圾回收。
在一个 except 子句体被执行之前,有关异常的详细信息存放在 sys
模块中,可通过 sys.exc_info()
来访问。 sys.exc_info()
返回一个 3 元组,由异常类、异常实例和回溯对象组成(参见 标准类型层级结构 一节),用于在程序中标识异常发生点。 当从处理异常的函数返回时 sys.exc_info()
的值会恢复为(调用前的)原值。
如果控制流离开 try
子句体时没有引发异常,并且没有执行 return
, continue
或 break
语句,可选的 else
子句将被执行。 else
语句中的异常不会由之前的 except
子句处理。
如果存在 finally
,它将指定一个‘清理’处理程序。 try
子句会被执行,包括任何 except
和 else
子句。 如果在这些子句中发生任何未处理的异常,该异常会被临时保存。 finally
子句将被执行。 如果存在被保存的异常,它会在 finally
子句的末尾被重新引发。 如果 finally
子句引发了另一个异常,被保存的异常会被设为新异常的上下文。 如果 finally
子句执行了 return
, break
或 continue
语句,则被保存的异常会被丢弃:
- >>> def f():
- ... try:
- ... 1/0
- ... finally:
- ... return 42
- ...
- >>> f()
- 42
在 finally
子句执行期间,程序不能获取异常信息。
当 return
, break
或 continue
语句在一个 try
…finally
语句的 try
子语句体中被执行时,finally
子语句也会‘在离开时’被执行。
函数的返回值是由最后被执行的 return
语句所决定的。 由于 finally
子句总是被执行,因此在 finally
子句中被执行的 return
语句总是最后被执行的:
- >>> def foo():
- ... try:
- ... return 'try'
- ... finally:
- ... return 'finally'
- ...
- >>> foo()
- 'finally'
有关异常的更多信息可以在 异常 一节找到,有关使用 raise
语句生成异常的信息可以在 raise 语句 一节找到。
在 3.8 版更改: 在 Python 3.8 之前,continue
语句不允许在 finally
子句中使用,这是因为具体实现存在一个问题。