暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Python 基础知识整理 - 错误和异常(2)

数据库杂货铺 2021-12-20
686

抛出异常

 

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 子句执行 breakcontinue return 语句,则不会重新引发异常。

 

 如果 try 语句碰到 breakcontinue returnfinally 子句将只执行到 breakcontinue 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


文章转载自数据库杂货铺,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论