Python中的try语句是一种异常处理机制,用于捕获和处理可能会导致程序崩溃的异常。try语句包含两个关键字:try和except。try块中包含可能引发异常的代码,而except块则包含处理异常的代码。
一、try语句语法
try 语句可为一组语句指定异常处理句柄和/或清理代码:
try_stmt ::= try1_stmt | try2_stmt | try3_stmt try1_stmt ::= "try" ":" suite ("except" [expression ["as" identifier]] ":" suite)+ ["else" ":" suite] ["finally" ":" suite] try2_stmt ::= "try" ":" suite ("except" "*" expression ["as" identifier] ":" suite)+ ["else" ":" suite] ["finally" ":" suite] try3_stmt ::= "try" ":" suite "finally" ":" suite
二、except子句
当try语句块中发生异常时,Python将启动对except子句的搜索,逐一检查每个except子句以找到与该异常匹配的子句,并执行相应的处理代码。
- 如果存在一个无表达式的except子句,它将匹配任何异常。而对于带有表达式的except子句,该表达式会被求值,如果结果对象与发生的异常“兼容”,则该子句将匹配该异常;
- 如果一个对象是该异常对象所属的类或是其非虚拟基类,或者是包含异常对象的类或该异常对象的非虚拟基类条目的元组,则此对象也是与该异常兼容的;
- 如果没有 except 子句与异常相匹配,则会在周边代码和发起调用栈上继续搜索异常处理句柄;
- 如果在对 except 子句头部的表达式求值时引发了异常,则对处理句柄的原始搜索会被取消并在周边代码和调用栈上启动对新异常的搜索(它会被视作是整个 try 语句所引发的异常)。
当带到一个匹配的 except 子句时,异常将被赋值给该 except 子句在 as 关键字之后指定的目标,如果存在此关键字的话,并且该 except 子句的代码块将被执行。 所有 except 子句都必须有可执行的代码块。 当到达此类代码块的末尾时,通常会转到整个 try 语句之后继续执行。 (这意味着如果对同一异常存在两个嵌套的处理句柄,并且异常发生在内层处理句柄的 try 子句中,则外层处理句柄将不会处理该异常。)
当使用 as target 来为异常赋值时,它将在 except 子句结束时被清除。 这就相当于:
except E as N: foo
被转写为:
except E as N: try: foo finally: del N
这意味着异常必须被赋值给一个不同的名称才能在 except 子句之后引用它。 异常会被清除是因为在附加了回溯信息的情况下它们会形成栈帧的循环引用,使得帧中的所有局部变量保持存活直到发生下一次垃圾回收。
在 except 子句的代码块被执行之前,异常将保存在 sys 模块中,在那里它可以从 except 子句的语句体内部通过 sys.exception() 被访问。 当离开一个异常处理句柄时,保存在 sys 模块中的异常将被重置为在此之前的值:
>>>print(sys.exception()) None >>>try: ... raise TypeError ...except: ... print(repr(sys.exception())) ... try: ... raise ValueError ... except: ... print(repr(sys.exception())) ... print(repr(sys.exception())) ... TypeError() ValueError() TypeError() >>>print(sys.exception()) None
三、except*子句
except* 子句被用来处理异常组(ExceptionGroup)。在使用异常组时,要匹配的异常类型将按照与except中相同的方式来解读。但是在使用异常组的情况下,当类型与组内的某些异常相匹配时,我们可以进行部分匹配。这意味着有多个except子句可以被执行,各自处理异常组的一部分。每个子句最多执行一次,并处理所有匹配异常中的一个异常组。组内的每个异常将至多由一个except子句来处理,即第一个与其匹配的子句。
>>>try: ... raise ExceptionGroup("eg", ... [ValueError(1), TypeError(2), OSError(3), OSError(4)]) ...except* TypeError as e: ... print(f'caught {type(e)} with nested {e.exceptions}') ...except* OSError as e: ... print(f'caught {type(e)} with nested {e.exceptions}') ... caught <class 'ExceptionGroup'> with nested (TypeError(2),) caught <class 'ExceptionGroup'> with nested (OSError(3), OSError(4)) + Exception Group Traceback (most recent call last): | File "<stdin>", line 2, in <module> | ExceptionGroup: eg +-+---------------- 1 ---------------- | ValueError: 1 +------------------------------------
任何未被 except* 子句处理的剩余异常最后都会在 except* 子句中被重新引发。 如果此列表包含一个以上的要被重新引发的异常,它们将被合并成一个异常组。
如果被引发的异常不是一个异常组并且其类型与某个 except* 子句相匹配,它将被捕获并由附带空消息字符串的异常组来包装。
>>>try: ... raise BlockingIOError ...except* BlockingIOError as e: ... print(repr(e)) ... ExceptionGroup('', (BlockingIOError()))
一个 except* 子句必须有一个匹配的类型,并且此类型不能是 BaseExceptionGroup 的子类。 不可以将 except 和 except* 在同一个 try 中混用。 break, continue 和 return 不可出现在 except* 子句中。
四、else子句
如果控制流离开 try 子句体时没有引发异常,并且没有执行 return, continue 或 break 语句,可选的 else 子句将被执行,else 语句中的异常不会由之前的 except 子句处理。
五、finally子句
在try语句块中,如果存在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'
六、自定义异常类
在 Python 中,可以通过继承 Exception 类创建自定义异常。自定义异常可以用于实现自定义的错误处理机制,例如输出更加明确的错误提示信息。
# 定义自定义异常类 class CustomException(Exception): def __init__(self, message): self.message = message try: x = input("请输入一个整数: ") if not x.isdigit(): raise CustomException("仅支持整数输入") except CustomException as e: print(e.message)