简介
在计算机科学和程序开发中,异常处理是响应异常(即需要特殊处理的异常或非正常情况)发生的过程。Python 拥有许多内置异常,当程序遇到错误时会触发这些异常。此外,许多外部库也包含它们自己的自定义异常,你同样需要对其进行处理。
在本实验中,你将学习 Python 异常处理的基础知识。你将从基础的 try 和 except 代码块开始,逐步学习如何处理多种异常、理解 finally 代码块的用途,最后创建并管理你自己的自定义异常。
基础异常处理
在这一步中,你将学习如何使用 try 和 except 代码块进行 Python 异常处理的基础知识。
数学上有一个公理:不能除以零。如果你在 Python 中尝试这样做,解释器会抛出一个内置的 ZeroDivisionError。让我们看看实际情况。
注意: 如果你不熟悉如何使用 WebIDE(类似 VS Code),请先通过此实验进行学习。
首先,从左侧的文件浏览器中打开 basic_handler.py 文件并添加以下代码。这段代码定义了一个简单的除法函数。
def divide(dividend , divisor):
print(dividend / divisor)
divide(dividend=10, divisor=5)
现在,在终端中运行该脚本:
python3 basic_handler.py
你将看到正确的输出:
2.0
接下来,让我们看看尝试除以零时会发生什么。修改 basic_handler.py 中的最后一行,将除数改为 0 来调用 divide 函数:
def divide(dividend , divisor):
print(dividend / divisor)
divide(dividend=10, divisor=0)
再次运行脚本:
python3 basic_handler.py
这一次,程序崩溃并打印出回溯信息(traceback),指出了 ZeroDivisionError:
Traceback (most recent call last):
File "/home/labex/project/basic_handler.py", line 4, in <module>
divide(dividend=10, divisor=0)
File "/home/labex/project/basic_handler.py", line 2, in divide
print(dividend / divisor)
ZeroDivisionError: division by zero
为了防止程序崩溃并显示用户友好的提示信息,我们可以使用 try 和 except 来「处理」异常。可能导致错误的代码放在 try 代码块中,而如果错误发生时需要执行的代码则放在 except 代码块中。
更新 basic_handler.py 文件,使用以下代码优雅地处理 ZeroDivisionError:
## try-except: 优雅地处理异常
def divide(dividend , divisor):
try: ## 尝试执行此代码
print(dividend / divisor)
except ZeroDivisionError: ## 捕获特定类型的异常
print('You can not divide by 0')
divide(dividend=10, divisor=0)
现在,再次运行脚本:
python3 basic_handler.py
程序不再崩溃,而是打印出了你定义的友好提示信息,并且程序执行正常结束:
You can not divide by 0
在一个异常块中处理多种异常
在这一步中,你将了解如何在一个 except 代码块中处理多种不同的异常类型。当可能发生多种类型的错误时,这对于简化错误处理代码非常有用。
你可以捕获一个异常元组,例如 (ZeroDivisionError, TypeError),从而用相同的逻辑处理它们。
打开 multiple_handler.py 文件并添加以下代码。该函数旨在触发 TypeError(通过尝试连接字符串和整数)或 ZeroDivisionError。
## 在一个 except 块中处理多种异常
def divide(dividend , divisor):
try:
if (dividend == 10):
var = 'str' + 1 ## 这将引发 TypeError
else:
print(dividend / divisor)
except (ZeroDivisionError, TypeError) as error: ## 捕获多种异常类型
print(error) ## 打印错误信息
divide(dividend=20, divisor=5)
首先,让我们在没有异常发生的情况下运行代码。执行脚本:
python3 multiple_handler.py
输出将是除法的结果:
4.0
现在,让我们触发 TypeError。修改 multiple_handler.py 的最后一行,使用 10 作为被除数:
## ... (其余代码) ...
divide(dividend=10, divisor=5)
再次运行脚本:
python3 multiple_handler.py
except 代码块捕获了 TypeError 并打印了相关的错误信息:
can only concatenate str (not "int") to str
最后,让我们触发 ZeroDivisionError。再次修改最后一行,这次尝试除以零。请注意,此时 TypeError 的条件 (dividend == 10) 不满足。
## ... (其余代码) ...
divide(dividend=20, divisor=0)
运行脚本:
python3 multiple_handler.py
同一个 except 代码块现在捕获了 ZeroDivisionError 并打印了它的信息:
division by zero
为了通过验证,请确保脚本中的最后一次调用是 divide(dividend=10, divisor=5) 以触发 TypeError。
异常处理中的 Finally 代码块
在这一步中,你将了解 finally 代码块的目的和用法。finally 子句中的代码无论是否发生或捕获了异常,都会被执行。这使得它非常适合进行清理操作,例如关闭文件或网络连接。
让我们看看它是如何工作的。打开 finally_handler.py 文件并添加以下代码:
## finally 块:无论是否发生异常,始终执行
def divide(dividend , divisor):
try:
print(dividend / divisor)
except ZeroDivisionError:
print('You can not divide by 0')
finally: ## 始终执行,即使发生异常
print('Execution finished')
divide(dividend=10, divisor=5)
在终端中运行脚本:
python3 finally_handler.py
在这种情况下,没有发生异常。try 代码块成功完成,然后执行 finally 代码块。
2.0
Execution finished
现在,让我们引入一个错误。修改 finally_handler.py 中的最后一行,尝试除以零:
## ... (其余代码) ...
divide(dividend=10, divisor=0)
再次运行脚本:
python3 finally_handler.py
这一次,抛出了 ZeroDivisionError 并被 except 代码块捕获。即使发生了异常,finally 代码块仍然会在 except 代码块执行完毕后运行。
You can not divide by 0
Execution finished
正如你所见,「Execution finished」消息在两种情况下都被打印了出来,这证明了 finally 代码块是放置必须始终运行的代码的可靠位置。
自定义异常
在这一步中,你将学习如何创建自己的自定义异常。这是一个强大的功能,可以使你的应用程序更具可读性和可维护性,因为你可以创建描述程序领域特有问题的特定错误类型。
自定义异常是通过创建一个继承自 Python 内置 Exception 类的新类来定义的。然后,你可以使用 raise 关键字触发此异常。
打开 custom_exception.py 文件并添加以下代码来定义并抛出一个简单的自定义异常:
## 自定义异常:通过继承 Exception 类创建
class MyCustomException(Exception):
pass
raise MyCustomException ## 抛出自定义异常
如果你运行此脚本,它将立即停止并显示新异常的回溯信息:
python3 custom_exception.py
Traceback (most recent call last):
File "/home/labex/project/custom_exception.py", line 5, in <module>
raise MyCustomException ## 抛出自定义异常
__main__.MyCustomException
你也可以在抛出自定义异常时提供描述性消息。修改 custom_exception.py,向异常传递一个字符串:
class MyCustomException(Exception):
pass
raise MyCustomException('A custom message for my custom exception')
运行此代码将在回溯信息中显示你的消息:
Traceback (most recent call last):
File "/home/labex/project/custom_exception.py", line 4, in <module>
raise MyCustomException('A custom message for my custom exception')
__main__.MyCustomException: A custom message for my custom exception
处理自定义异常的方法与处理内置异常相同:使用 try...except 代码块。将 custom_exception.py 更新为最终形式,即抛出并处理该自定义异常:
class MyCustomException(Exception):
pass
try:
raise MyCustomException('A custom message for my custom exception')
except MyCustomException:
print('My custom exception was raised')
现在,最后一次运行脚本:
python3 custom_exception.py
程序不再崩溃。相反,它执行了 except 代码块中的代码并打印了你的确认消息:
My custom exception was raised
总结
在本实验中,你学习了 Python 异常处理的基本技术。你从基础的 try 和 except 代码块开始,学会了如何捕获特定错误并防止程序崩溃。接着,你学习了如何在一个 except 代码块中高效地处理多种异常类型。你还探索了 finally 代码块,它对于必须始终运行的清理代码至关重要。最后,你掌握了创建、抛出和处理自定义异常的能力,从而能够在应用程序中构建更健壮、更具描述性的错误处理逻辑。



