- 来源 | 愿码 (ChainDesk.CN) 内容编辑
- 愿码 Slogan | 连接每个程序员的故事
- 网站 | http://chaindesk.cn
- 愿码愿景 | 打造全学科 IT 系统免费课程,助力小白用户、初级工程师 0 成本免费系统学习、低成本进阶,帮助 BAT 一线资深工程师成长并利用自身优势创造睡后收入。
- 官方公众号 | 愿码 | 愿码服务号 | 区块链部落
- 免费加入愿码全思维工程师社群 | 任一公众号回复“愿码”两个字获取入群二维码
本文阅读时长:10min
本文所涉及知识点
- Python 中有哪些异常?
- 使用 try … except 子句控制程序流
- 通过处理异常来处理常见问题
- 创建和使用自定义异常类
在直接进入代码并解决这些问题之前,让我们首先了解异常是什么以及处理异常是什么意思。
什么是异常?
异常是 Python 中的对象。它为我们提供了有关在程序执行期间检测到的错误的信息。在调试应用程序时注意到的错误是 未处理的异常,因为我们没有这些异常。在本文后面,您将学习处理这些异常的技巧。
在早期回溯中看到的 ValueError 和 IndexError 异常是 Python 中内置异常类型的示例。在下一节中,您将了解 Python 支持的其他一些内置异常。
最常见的异常
让我们快速回顾一些最常遇到的异常。最简单的方法是尝试运行一些错误的代码,让它报告错误回溯的问题!启动 Python 解释器并编写以下代码:
以下是一些异常情况:
正如您所看到的,代码的每一行都会抛出一个带有异常类型的错误回溯(突出显示)。这些是 Python 中的一些内置异常。Python 提供 BaseException 作为所有内置异常的基类。但是,大多数内置异常不直接继承 BaseException。相反,它们是从一个名为 Exception 的类派生而来的,而这个类又继承自 BaseException。处理程序退出的内置异常(例如,SystemExit)直接从 BaseException 派生。您还可以创建自己的异常类作为 Exception 的子类。您将在本文后面了解到这一点。
异常处理
到目前为止,我们已经看到了异常的发生方式 现在,是时候学习如何使用 try … except 子句来处理这些异常。以下伪代码显示了 try … except 子句的一个非常简单的示例:
我们来看看前面的代码片段:
· 首先,程序尝试执行 try 子句中的代码。
· 在执行期间,如果出现错误(如果发生异常),它将跳出此 try 子句。try 块中的其余代码不会被执行。
· 然后,它在 except 子句中查找适当的异常处理程序并执行它。
这里使用的 except 子句是通用的。它将捕获 try 子句中发生的所有类型的异常。而不是拥有这个“全能”处理程序,更好的做法是捕获您预期的错误并编写特定于这些错误的异常处理代码。例如,try 子句中的代码可能会抛出 AssertionError。您可以编写特定的异常处理程序,而不是使用 universal except 子句,如下所示:
在这里,我们有一个 except 子句专门处理 AssertionError。它还意味着除了 AssertionError 之外的任何错误都将作为未处理的异常漏掉。为此,我们需要使用不同的异常处理程序定义多个 except 子句。但是,在任何时候,只会调用一个异常处理程序。用一个例子可以更好地解释这一点。我们来看看下面的代码片段:
该试块调用 solve_something()。此函数接受一个数字作为用户输入,并断言该数字大于零。如果断言失败,它会直接跳转到处理程序,但 AssertionError 除外。
在另一个场景中,如果 > 0,则执行 solve_something()中的其余代码。您会注意到未定义变量 x,这会导致 NameError。此异常由另一个异常子句处理,但 NameError 除外。同样,您可以为预期的错误定义特定的异常处理程序。
提高并重新提出异常
Python 中的 raise 关键字用于强制发生异常。换句话说,它引发了一个异常。语法很简单; 只需打开 Python 解释器并输入:
>>> raise AssertionError("some error message")
这会产生以下错误回溯:
Traceback (most recent call last):
File "", line 1, in
AssertionError : some error message
在某些情况下,我们需要重新引发异常。假设,在 try 子句中,您有一个将数字除以零的表达式。在普通算术中,这个表达没有意义。这是一个错误!这会导致程序引发一个名为 ZeroDivisionError 的异常。如果没有异常处理代码,程序将只打印错误消息并终止。
如果您希望将此错误写入某个日志文件然后终止该程序,该怎么办?在这里,您可以使用 except 子句首先记录错误。然后,使用不带任何参数的 raise 关键字来重新引发异常。异常将在堆栈中向上传播。在此示例中,它终止程序。可以使用 raise 关键字重新引发异常而不使用任何参数。
这是一个示例,显示如何重新引发异常:
可以看出,在解决 a / b 表达式时,会出现 zeroexception 的 adivision。这是因为变量 b 的值设置为 0。出于说明目的,我们假设此错误没有特定的异常处理程序。因此,我们将使用 general except 子句,在记录错误后重新引发异常。如果您想自己尝试,只需在新的 Python 文件中编写前面说明的代码,然后从终端窗口运行它。以下屏幕截图显示了上述代码的输出:
try … except
可以在 try … except 子句中指定可选的 else 块。在其他的只发生 ifno 异常块被执行的尝试 …… 除了条款。语法如下:
在其他块的前执行最后条款,我们将在接下来的学习。
finally……clean it up!
还有其他东西可以添加到 try … 除了 … else story:一个可选的 finally 子句。顾名思义,此子句中的代码在关联的 try … except 块的末尾执行。无论是否引发异常,finally 子句(如果指定)将在 try … except 子句的末尾执行。想象一下它是由 Python 提供的全天候保证!以下代码段显示了 finally 块的运行情况:
运行这个简单的代码将产生以下输出:
$ python finally_example1.py
Enter a number: -1
Uh oh..Assertion Error.
Do some special cleanup
输出中的最后一行是 finally 子句中的 print 语句。
带有和不带 finally 子句的代码片段如下面的屏幕截图所示。即使 except 子句指示代码从函数返回,也确保 finally 子句中的代码最终执行。
在最后条款通常用于离开功能之前执行清理任务。示例用例是关闭数据库连接或文件。但请注意,为此,您还可以在 Python 中使用 with 语句。
编写一个新的异常类
创建一个从 Exception 派生的新异常类是微不足道的。打开 Python 解释器并创建以下类:
>>> class GameUnitError(Exception):
... pass
...
>>>
就这样!我们有一个新的异常类 GameUnitError,可以部署了。如何测试此异常?在 Python 解释器中键入以下代码行:
>>> raise GameUnitError("ERROR: some problem with game unit")
引发新创建的异常将打印以下回溯:
>>> raise GameUnitError("ERROR: some problem with game unit")
Traceback (most recent call last):
File "", line 1, in
__main__.GameUnitError: ERROR: some problem with game unit
将 GameUnitError 类复制到其自己的模块 gameuniterror.py 中,并将其保存在与 attackoftheorcs_v1_1.py 相同的目录中。
接下来,更新 attackoftheorcs_v1_1.py 文件以包含以下更改:
首先,在文件的开头添加以下 import 语句:
from gameuniterror import GameUnitError
第二个变化是在 AbstractGameUnit.heal 方法中。更新后的代码显示在以下代码段中。观察高亮代码,只要提出的价值自定义异常 self.health_meter 超过的 self.max_hp。
通过这两个更改,运行之前创建的 heal_exception_example.py。您将看到引发新的异常,如以下屏幕截图所示:
扩展异常类
我们可以用 GameUnitError 类做更多的事情吗?当然!就像任何其他类一样,我们可以定义属性并使用它们。让我们进一步扩展这个课程。在修改后的版本中,它将接受一个额外的参数和一些预定义的错误代码。更新的 GameUnitError 类显示在以下屏幕截图中:
我们来看看前面屏幕截图中的代码:
· 首先,它调用 Exception 超类的__init__方法,然后定义一些额外的实例变量。
· 一个新的 dictionary 对象 self。error_dict 将错误整数代码和错误信息保存为键值对。
· 该 self.error_message 存储有关根据提供的错误代码当前错误的信息。
· 在尝试 …… 除了子句确保 error_dict 实际上已经由指定的键码的说法。它不在 except 子句中,我们只是检索默认错误代码为 000 的值。
到目前为止,我们已经对 GameUnitError 类和 AbstractGameUnit.heal 方法进行了更改。我们还没有完成。拼图的最后一块是修改主要在程序 heal_exception_example.py 文件。代码显示在以下屏幕截图中:
我们来看看代码:
· 由于 heal_by 值太大,try 子句中的 heal 方法会引发 GameUnitError 异常。
· new except 子句处理 GameUnitError 异常,就像任何其他内置异常一样。
· 在 except 子句中,我们有两个 print 语句。第一个打印 health_meter> max_hp!(回想一下,当在 heal 方法中引发此异常时,此字符串被作为 GameUnitError 实例的第一个参数给出)。第二个 print 语句检索并打印 GameUnitError 实例的 error_message 属性。
我们已经做好了所有的改变。我们可以在终端窗口中运行此示例:
$ python heal_exception_example.py
该程序的输出显示在以下屏幕截图中:
在这个简单的例子中,将错误信息打印到控制台。您可以进一步将详细错误日志写入文件,并跟踪应用程序运行时生成的所有错误消息。