如何在Python中捕获多个异常?

2025 年 9 月 9 日 | 阅读 12 分钟

多个异常处理是 Python 中的一种方法,用于管理和响应代码执行过程中发生的各种类型的错误和调试。我们将解释多个异常的捕获机制并识别我们所见的内容。要深入了解此主题,您必须熟悉 Python 的异常处理。

Python 在代码遇到不常见但可预见的错误时会引发异常,例如尝试打开一个不存在的文件。您应该编写代码来处理这些错误。

错误(bug)发生在代码执行了不合逻辑的操作时,例如计算错误。错误需要被修复和移除。这个过程称为调试,它对程序至关重要。

当 Python 程序遇到错误并引发异常时,可能会导致程序崩溃,但在此之前,Python 会提供一个回溯(Traceback)消息,让我们了解问题所在。这是一个简单的例子。

示例 -

代码 10 / "One" 会引发一个 TypeError,并带有消息 "unsupported operand type(s) for /: 'int' and 'str'"。Python 尝试执行除法运算,但无法将整数

除以字符串,因此它会引发 TypeError,因为该操作不受支持。为了处理异常并防止代码在发生错误时崩溃,您可以使用 Python 中的 try 语句。此语句允许您监视代码中的异常,并在它们发生时采取适当的操作。这有助于提供更顺畅、用户友好的体验。

示例 -

输出

Enter first number 32
1Enter second number 16
32.0 divided by 16.0 is 2.0
Enter first number 23
Enter second number 0
You can't divide by zero
Enter first number 12
Enter second number "five"
Please enter valid numbers

在上面的代码中,我们首先将用户输入捕获为字符串,然后尝试在 try 块中将其转换为浮点数。我们还在执行除法运算之前显式检查了除以零的情况。此外,我们添加了一个捕获所有情况的 except 块,以处理代码执行期间可能发生的任何意外错误。

处理多个 Python 异常

当您需要对每个特定异常执行不同的操作时,使用单独的 except 子句处理单个异常是合适的。但是,如果您注意到您对多个异常执行相同的操作,则可以通过将多个异常组合到一个 except 子句中来创建更简洁、更易读的代码。要实现这一点,您可以在 except 语句中将异常指定为元组。

现在,我们将重写上面的代码以在单行中处理异常。让我们来理解下面的例子。

示例 -

输出

在情况 1 中,输出将是

Enter first number: 12
Enter second number: 6
12.0 divided by 6.0 is 2.0

在情况 2 中,输出将是

Enter first number: 5
Enter second number: 0
Oops! You can't divide the number by 0

在上面的示例中,我们可以使用单个 `except` 子句来处理 `ValueError` 和 `ZeroDivisionError` 异常。类似地,如果需要,我们可以添加其他 `except` 子句来处理其他异常,以防万一。

在初始测试中,只有 try 块运行,因为没有引发异常。在第二和第三个测试用例中,我们分别故意触发了 ValueError 和 ZeroDivisionError。我们已经通过同一个 except 子句有效地看到了这两种异常。在这两种情况下,程序都遵循 try,然后 except(处理 ValueError 或 ZeroDivisionError)的模式。正如我们所见,处理程序能够优雅地管理这两种异常。

识别捕获的 Python 异常

在面向对象编程的概念中,类充当模板,定义了实例化对象属性和功能。当您的代码引发 Python 异常时,它实际上是从定义该异常的类创建一个对象。例如,当引发 ValueError 异常时,您实际上是在创建 ValueError 类的实例。

您不必深入了解面向对象编程即可处理异常,但了解不同类型的异常来自不同的类会很有帮助。简而言之,异常就像工具箱中的不同工具,每种工具在代码出错时都有其独特的用途。

示例 -

输出

Enter first number: 10
Enter second number: 2
10.0 divided by 2.0 is 5.0
Enter first number: 23
Enter second number: "two"
A ValueError has occurred.
Enter first number: 10
Enter second number: 0
A ZeroDivisionError has occurred.

假设您想通过添加乘法功能来改进代码。您还可以选择在用户尝试执行不受支持或意外的操作(例如按 Enter 键)时自行引发 RuntimeError。

让我们理解以下示例 -

示例 -

输出

Enter your first number: 10
Enter your second number: "h"
An error occurred: could not convert string to float: '"h"'
Enter a valid numbers.

在此更新版本中,我们使用了 operator 模块中的 mul() 和 truediv() 函数来执行乘法和除法运算。这些函数以及两个数值输入一起作为参数传递给 calculate() 函数。在 calculate() 函数内部,根据所选的操作,会调用合适的运算符函数,该函数在 mul() 和 truediv() 之间选择。此过程成功执行计算,但它依赖于提供两个数值输入并指定 '*' 或 '/' 作为操作。

当用户输入不受支持的运算符时,您的代码会故意触发 RuntimeError。如果未能正确处理此错误,代码可能会崩溃。

在 `except` 部分,我们指定了一个异常元组来捕获,并将捕获的异常分配给名为 `error` 的变量。最初,代码会打印异常类的名称,无论引发的是哪个异常。类似地,您可以使用 match 块根据正在处理的特定异常显示特定消息。这样,您就可以根据遇到的错误类型向用户提供相关的错误消息。

使用超类处理多个可能的 Python 异常

在 Python 中,各种异常是不同类的实例,所有这些类都属于 Python 异常类层次结构。每个 Python 异常都继承自一个名为 BaseException 的类,在该层次结构中,存在一个称为 Exception 类的超类。Base Exception 类作为异常的父类。

在异常中,继承基本上涉及将异常组织成一个层次结构。例如,我们可以注意到 ArithmeticError 是 exception 的子类。这些类之间的区别很小。

OSError 类有两个子类:PermissionError 和 FileNotFoundError。这证明了 PermissionError 和 FileNotFoundError 都是 OSError 的子类,同样,由于 OSError 继承自 Exception,它们也被视为异常子类。

示例

说明

代码编写为打印名为 datafile.txt 的文件内容,但这仅在文件存在时才有效。如果找不到 datafile.txt,上述代码将引发 FileNotFoundError。虽然您有一个 except 子句似乎可以捕获 OSError,但由于 FileNotFoundError 是 OSError 的一种,它也可以处理 FileNotFoundError。

要确定我们遇到的 OSError 的具体子类,我们可以使用 `type(error).__name__` 方法来打印其类名。然而,这对于大多数用户来说可能并不重要。或者,您可以通过检查 `.errno` 属性来查看底层错误。此属性包含操作系统生成的数值代码。它提供了有关触发 OSError 的问题的性质的见解。

使用 contextlib.suppress() 处理多个 Python 异常

在某些情况下,代码可能会遇到各种需要忽略的异常,以确保代码的功能。Python 中处理异常的传统方法是捕获它们而不采取任何具体操作。如果按照这种方式执行,代码将如下所示:

示例

说明

为了处理可能发生的 FileNotFoundError(如果文件不存在),我们像往常一样捕获异常,并在处理程序中使用“pass”关键字来忽略它。在这些条件下运行我们的代码不会做任何事情,因为它不会产生任何输出,重要的是,我们的程序不会意外终止。

通过在我们代码中使用这种方法,我们可能会让读者感到困惑。我们基本上指示程序捕获异常,然后完全忽略该异常。

示例

说明

在此代码中,我们使用了 suppress() 函数来创建一个上下文管理器,该管理器旨在处理 FileNotFoundError 和 PermissionError 异常。然后,with 语句将我们建立的上下文管理器应用于其中的代码。如果我们引发了另一种类型的异常,代码仍然可以正常运行而不会崩溃。但是,请注意,如果将任何代码放在“with”块之外,则异常将不起作用,这意味着它将无效。

让我们看另一个场景:想象我们有多个名为“transactions.txt”的文件,这些文件在一天中的不同时间创建。这些文件包含我们需要累积到单个文件“all_transactions.txt”中的数据。合并后,代码会将原始文件存档到“transactions.arch_ts”,其中“ts”代表时间戳以确保唯一性。但是,有时我们的程序会搜索一个根本不存在的文件。

示例

说明

此代码会反复检查“transactions.txt”文件中是否有新事务,将它们附加到“all_transactions.txt”文件,并用时间戳存档原始文件。它使用 suppress 来毫无问题地处理 FileNotFoundError,即使临时文件不存在。

使用异常组捕获多个 Python 异常

当我们在代码中使用 try-except 块时,它可以捕获 try 块中发生的初始异常。如果我们尝试引发多个异常,程序将在处理第一个异常后终止,并且任何其他类似的异常都不会被引发。我们可以通过下面的代码看到一个实际示例。

让我们看一个例子来正确理解上述陈述

示例

输出

ZeroDivisionError occurred 1 times.
FileNotFoundError occurred 0 times.
NameError occurred 0 times.	

在上面的代码中,名为 ZeroDivisionError()、FileNotFoundError() 和 NameError() 的三个变量被初始化为零。

在 try 块内部,一个循环遍历异常列表并引发每个异常。但是,代码一次只捕获和处理一个异常。

在 except 块中,有单独的子句用于 ZeroDivisionError、FileNotFoundError 和 NameError。最后,finally 块打印引发的每种异常的计数。

结论

本教程涵盖了我们在 Python 中捕获多个异常的方法,我们学习了多个异常处理,这是一种在代码执行过程中用于管理和响应各种类型的错误和调试的 Python 方法。

我们强调了管理错误以防止程序崩溃的重要性,而 `try` 和 `except` 语句用于处理异常,它们向我们展示了如何有效地处理多个异常。正确的异常处理可确保代码的可靠性和用户友好的错误消息,从而提高程序的整体效率和健壮性。

如何在 Python 中捕获多个异常的常见问题解答

1. 为什么我们需要捕获多个异常?

有时一段代码可能会引发不同类型的错误,妥善处理它们可以防止崩溃,并允许我们根据异常做出不同的响应。

2. 如何在 Python 中捕获多个异常?

我们可以在单个 except 块中使用元组语法。让我们通过一个例子来理解这个陈述。

3. 能否用单独的 except 块处理多个异常?

是的,我们通常可以处理多个异常,因为每个 except 都可以处理不同类型的错误。让我们通过一个例子来理解这一点。

4. 能否将所有异常一起捕获?

是的,我们可以通过使用 `except Exception as e` 来一起捕获所有异常,这被称为大多数异常的基类。

5. 我可以忽略一个异常而不处理它吗?

是的,我们可以通过使用 `pass` 关键字来忽略一个异常而不处理它。

6. 捕获多个异常时可以使用变量名吗?

是的,我们通过使用 `as` 关键字在捕获多个异常时可以使用变量名。

示例

输出

Caught: list index out of range

7. 能否在多个 except 块中同时使用 else 和 finally?

是的,我们可以在多个块中同时使用 else 和 finally。让我们通过一个例子来理解这一点。

示例

输出

No exception has occurred
Always executed