说明
《Python 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
在Python中,try 语句是用于捕获和处理异常的结构。它允许你编写可能会引发异常的代码块,并提供一种处理这些异常的方式,以避免程序的崩溃。
以下是一个简单的例子:
try:
x = int(input("请输入一个整数: "))
result = 10 / x
print("结果是:", result)
except ValueError:
print("输入不是有效的整数")
except ZeroDivisionError:
print("除以零错误")
else:
print("没有发生异常")
finally:
print("总是执行的代码块")
上例中,用户被要求输入一个整数,然后程序尝试计算 10 除以用户输入的整数。如果用户输入的是非整数,会引发 ValueError 异常;如果用户输入的是零,会引发 ZeroDivisionError 异常。无论是否发生异常,finally 语句块中的代码都会执行。
except 语句可以有多个,每个对应不同类型的异常。你还可以使用 as 关键字将异常对象赋值给一个变量,以便在 except 语句块中使用。
try 语句的基本结构如下:
try:
# 可能引发异常的代码块
# 如果发生异常,控制流将转移到 except 语句块
# 如果没有异常,except 语句块将被跳过
# 可能包含多个语句块
except ExceptionType1 as variable1:
# 处理 ExceptionType1 类型的异常
# variable1 是异常对象的引用
except ExceptionType2 as variable2:
# 处理 ExceptionType2 类型的异常
# variable2 是异常对象的引用
# 可选的 else 语句块,当没有异常发生时执行
else:
# 在没有异常发生时执行的代码
# 可选的 finally 语句块,无论是否发生异常都会执行
finally:
# 总是会执行的代码
except 子句指定一个或多个异常处理句柄。 当 try 子句中无异常发生时,将不会有任何异常处理句柄被执行。 当 try 子句代码块中发生异常时,将启动对异常处理句柄的搜索。 此搜索会逐一检查 except 子句直至找到与该异常相匹配的子句为止。 如果存在无表达式的 except 子句,则它必须是最后一个;它将匹配任何异常。 对于带有表达式的 except 子句,该表达式会被求值,如果结果对象与发生的异常“兼容”则该子句将匹配该异常。 如果一个对象是该异常对象所属的类或是其 非虚拟基类,或者是包含异常对象的类或该异常对象的非虚拟基类条目的元组,则此对象就是与该异常兼容的。
如果没有 except 子句与异常相匹配,则会在周边代码和发起调用栈上继续搜索异常处理句柄。 (异常会被传播给发起调用栈,除非存在一个 finally 子句正好引发了另一个异常。 新引发的异常将导致旧异常的丢失。)
如果在对 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*
子句中。
如果控制流离开 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'
在 3.8 版更改: 在 Python 3.8 之前,continue 语句不允许在 finally 子句中使用,这是因为具体实现中存在一个问题。
更新时间:2023-12-23 18:33:01 标签:python 异常