抛出异常
raise 语句允许程序员强制引发指定的异常。例如:
raise NameError('Hi')
Traceback (most recent call last):
File "C:\Python310\2.py", line 1, in <module>
raise NameError('Hi')
NameError: Hi
复制
raise 的唯一参数表示要引发的异常。这必须是异常实例或异常类(从 Exception 派生的类)。如果传递了异常类,则将通过调用其构造函数(不带参数)隐式实例化该异常类:
raise ValueError # 'raise ValueError()' 的简写
Traceback (most recent call last):
File "C:\Python310\2.py", line 1, in <module>
raise ValueError # 'raise ValueError()' 的简写
ValueError
复制
如果需要确定是否引发了异常但不打算处理它,则 raise 语句的更简单形式允许重新引发异常:
try:
raise NameError('Hi')
except NameError:
print('发生异常!')
raise
发生异常!
Traceback (most recent call last):
File "C:\Python310\2.py", line 2, in <module>
raise NameError('Hi')
NameError: Hi
复制
异常链
raise 语句允许可选的 from,这就启用了异常链。例如:
# exc 必须是异常实例或者 None
raise RuntimeError from exc
复制
这在转换异常时非常有用。例如:
def func():
raise IOError
try:
func()
except IOError as exc:
raise RuntimeError('无法连接到数据库') from exc
复制
运行结果:
Traceback (most recent call last):
File "C:\Python310\2.py", line 5, in <module>
func()
File "C:\Python310\2.py", line 2, in func
raise IOError
OSError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:\Python310\2.py", line 7, in <module>
raise RuntimeError('无法连接到数据库') from exc
RuntimeError: 无法连接到数据库
复制
当在 except 或 finally 部分引发异常时,异常链会自动发生。可以使用 from None 禁用异常链:
try:
open('database.sqlite')
except OSError:
raise RuntimeError from None
复制
运行结果:
Traceback (most recent call last):
File "C:\Python310\2.py", line 4, in <module>
raise RuntimeError from None
RuntimeError
复制
用户定义异常
在程序中可以通过创建新的异常类来指定自己的异常。异常通常应直接或间接地从 Exception 类派生。
定义的异常类能执行任何其他类可以执行的操作,但通常保持简单,通常只提供一些属性,这些属性允许异常处理程序提取有关错误的信息。在创建可能引发多个不同错误的模块时,通常的做法是为该模块定义的异常创建基类,并为不同错误条件创建特定异常类的子类:
class Error(Exception):
"""模块中的异常类基类。"""
pass
class InputError(Error):
"""输入异常
Attributes:
expression -- 错误发生的输入表达式
message -- 错误解释
"""
def __init__(self, expression, message):
self.expression = expression
self.message = message
class TransitionError(Error):
"""当操作尝试不允许的状态转换时引发。
Attributes:
previous -- 转换开始时的状态
next -- 尝试的新状态
message -- 解释不允许指定转换的原因
"""
def __init__(self, previous, next, message):
self.previous = previous
self.next = next
self.message = message
复制
大多数异常都是以 “Error” 结尾的名称定义的,与标准异常的命名类似。
许多标准模块定义自己的异常,以报告它们定义的函数中可能发生的错误。
定义清理行动
try 语句还有一个可选子句,用于定义在任何情况下都必须执行的清理操作。例如:
try:
raise KeyboardInterrupt
finally:
print('再见!')
复制
运行结果:
再见!
Traceback (most recent call last):
File "C:\Python310\2.py", line 2, in <module>
raise KeyboardInterrupt
KeyboardInterrupt
^C
复制
如果存在 finally 子句,finally 子句将作为 try 语句完成之前的最后一个任务执行。无论 try 语句是否生成异常,finally 子句都将运行。以下几点讨论了发生异常时更复杂的情况:
● 如果在 try 子句的执行过程中发生异常,则该异常可以由 except 子句处理。如果 except 子句未处理异常,则在执行 finally 子句后将重新引发异常。
● 执行 except 或 else 子句期间可能会发生异常。同样,在执行 finally 子句之后,将重新引发异常。
● 如果 finally 子句执行 break、continue 或 return 语句,则不会重新引发异常。
● 如果 try 语句碰到 break、continue 或 return,finally 子句将只执行到 break、continue 或 return 语句之前。
● 如果 finally 子句包含 return 语句,则返回值将是 finally 子句的 return 语句中的值,而不是 try 子句的 return 语句中的值。
例如:
def bool_return():
try:
return True
finally:
return False
复制
一个更复杂的例子:
def divide(x, y):
try:
result = x / y
except ZeroDivisionError:
print("除0!")
else:
print("结果是", result)
finally:
print("执行 finally 子句")
divide(2, 1)
复制
运行结果:
结果是 2.0
执行 finally 子句
复制
divide(2, 0)
复制
运行结果:
除0!
执行 finally 子句
复制
divide("2", "1")
复制
运行结果:
执行 finally 子句
Traceback (most recent call last):
File "C:\Python310\2.py", line 13, in <module>
divide("2", "1")
File "C:\Python310\2.py", line 3, in divide
result = x / y
TypeError: unsupported operand type(s) for /: 'str' and 'str'
复制
如你所见,finally 子句在任何情况下都会执行。通过除两个字符串引发的 TypeError 没有被 except 子句处理,因此在执行 finally 子句后重新引发。
在现实世界的应用程序中,finally 子句对于释放外部资源(如文件或网络连接)非常有用,无论资源的使用是否成功。
预定义的清理操作
有些对象定义了不再需要该对象时要执行的标准清理操作,而不管使用该对象的操作是否成功。请看下面的示例,它尝试打开一个文件并将其内容打印到屏幕上。
for line in open("2.txt", encoding='utf-8'):
print(line, end="")
复制
运行结果:
{"1": "吕布", "2" :"赵云"}
复制
这段代码的问题是,在这部分代码完成执行后,它会将文件保持打开状态一段时间。这在简单的脚本中不是一个问题,但在大型应用程序中可能是一个问题。with 语句允许以某种方式使用像文件这样的对象,以确保它们总是被及时、正确地清理。
with open("2.txt", encoding='utf-8') as f:
for line in f:
print(line, end="")
复制
语句执行后,文件 f 始终会关闭,即使在处理内容行时会遇到问题。类似文件,提供预定义清理操作的对象将在其文档中给出说明。
官方文档:
https://docs.python.org/3.9/tutorial/errors.html