如何在 Python 中检查异常是否为特定类型

PythonPythonBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

在这个实验中,你将学习如何在 Python 中检查异常是否属于特定类型,这是进行有效错误处理的一项关键技能。本实验重点在于理解异常层次结构,并利用这些知识来识别特定的异常类型。

本实验将引导你探索 Python 异常层次结构,从基类 BaseException 及其子类(如 Exception)开始。你将创建一个 Python 脚本 exception_hierarchy.py 来打印异常层次结构,这样你就能直观地看到不同异常类之间的关系。随后,你将运用这些知识,在后续步骤中使用 isinstance() 和直接类比较来检查异常类型。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/ErrorandExceptionHandlingGroup(["Error and Exception Handling"]) python/ErrorandExceptionHandlingGroup -.-> python/catching_exceptions("Catching Exceptions") python/ErrorandExceptionHandlingGroup -.-> python/raising_exceptions("Raising Exceptions") python/ErrorandExceptionHandlingGroup -.-> python/custom_exceptions("Custom Exceptions") subgraph Lab Skills python/catching_exceptions -.-> lab-559609{{"如何在 Python 中检查异常是否为特定类型"}} python/raising_exceptions -.-> lab-559609{{"如何在 Python 中检查异常是否为特定类型"}} python/custom_exceptions -.-> lab-559609{{"如何在 Python 中检查异常是否为特定类型"}} end

理解异常层次结构

在这一步中,你将学习 Python 中的异常层次结构。理解这个层次结构对于有效的错误处理至关重要。Python 中的异常以树状结构组织,顶部是基类,更具体的异常类从它继承而来。

所有异常的基类是 BaseException。直接从 BaseException 继承的有 ExceptionGeneratorExitKeyboardInterruptSystemExitException 类是大多数内置异常的超类,这些异常表示你的程序可能遇到的错误。

让我们创建一个 Python 脚本来探索这个层次结构。

  1. 打开你的 VS Code 编辑器。

  2. ~/project 目录下创建一个名为 exception_hierarchy.py 的新文件。

    ~/project/exception_hierarchy.py
  3. exception_hierarchy.py 文件中添加以下代码:

    def print_exception_hierarchy(exception_class, indent=0):
        """Prints the exception hierarchy starting from a given exception class."""
        print('  ' * indent + str(exception_class))
        for subclass in exception_class.__subclasses__():
            print_exception_hierarchy(subclass, indent + 1)
    
    print("Exception Hierarchy:")
    print_exception_hierarchy(Exception)

    这个脚本定义了一个递归函数 print_exception_hierarchy,它从 Exception 类开始打印异常层次结构。

  4. 在终端中使用以下命令运行脚本:

    python exception_hierarchy.py

    这将在终端中打印出异常层次结构的树状结构。

    示例输出:

    Exception Hierarchy:
    <class 'Exception'>
      <class 'ArithmeticError'>
        <class 'FloatingPointError'>
        <class 'OverflowError'>
        <class 'ZeroDivisionError'>
          <class 'decimal.DivisionByZero'>
      <class 'AssertionError'>
      <class 'AttributeError'>
      <class 'BufferError'>
      <class 'EOFError'>
      <class 'ImportError'>
        <class 'ModuleNotFoundError'>
        <class 'ZipImportError'>
      <class 'LookupError'>
        <class 'IndexError'>
        <class 'KeyError'>
          <class 'tracemalloc.DomainKey'>
      <class 'MemoryError'>
      <class 'NameError'>
        <class 'UnboundLocalError'>
      <class 'OSError'>
        <class 'BlockingIOError'>
        <class 'ChildProcessError'>
        <class 'ConnectionError'>
          <class 'BrokenPipeError'>
          <class 'ConnectionAbortedError'>
          <class 'ConnectionRefusedError'>
          <class 'ConnectionResetError'>
        <class 'FileExistsError'>
        <class 'FileNotFoundError'>
        <class 'InterruptedError'>
          <class 'InterruptedSystemCall'>
        <class 'IsADirectoryError'>
        <class 'NotADirectoryError'>
        <class 'PermissionError'>
        <class 'ProcessLookupError'>
        <class 'TimeoutError'>
        <class 'UnsupportedOperation'>
        <class 'itertools.Incomplete'>
        <class 'signal.ItimerError'>
      <class 'ReferenceError'>
      <class 'RuntimeError'>
        <class 'NotImplementedError'>
          <class 'asyncio.exceptions.IncompleteReadError'>
          <class 'zlib.error'>
        <class '_frozen_importlib._DeadlockError'>
        <class 'RecursionError'>
      <class 'StopAsyncIteration'>
      <class 'StopIteration'>
      <class 'SyntaxError'>
        <class 'IndentationError'>
          <class 'TabError'>
      <class 'SystemError'>
      <class 'TypeError'>
      <class 'ValueError'>
        <class 'UnicodeError'>
          <class 'UnicodeDecodeError'>
          <class 'UnicodeEncodeError'>
          <class 'UnicodeTranslateError'>
      <class 'Warning'>
        <class 'BytesWarning'>
        <class 'DeprecationWarning'>
        <class 'EncodingWarning'>
        <class 'FutureWarning'>
        <class 'ImportWarning'>
        <class 'PendingDeprecationWarning'>
        <class 'ResourceWarning'>
        <class 'RuntimeWarning'>
        <class 'SyntaxWarning'>
        <class 'UnicodeWarning'>
        <class 'UserWarning'>

    这个输出展示了异常的层次结构,以 Exception 作为基类,各种子类代表特定类型的错误。理解这个层次结构有助于你在适当的粒度级别捕获异常。例如,你可以捕获像 ZeroDivisionError 这样的特定异常,或者像 ArithmeticError(它是 ZeroDivisionError 的父类)这样更通用的异常。

对异常使用 isinstance() 函数

在这一步中,你将学习如何使用 isinstance() 函数来检查一个异常是否是某个特定类或类元组的实例。这对于灵活处理不同类型的异常很有用。

isinstance() 函数接受两个参数:一个对象和一个类信息(classinfo)。如果对象是类信息所指定类的实例,或者是该类的子类的实例,函数将返回 True;否则返回 False

让我们创建一个 Python 脚本来演示如何对异常使用 isinstance() 函数。

  1. 打开你的 VS Code 编辑器。

  2. ~/project 目录下创建一个名为 isinstance_exception.py 的新文件。

    ~/project/isinstance_exception.py
  3. isinstance_exception.py 文件中添加以下代码:

    try:
        result = 10 / 0
    except Exception as e:
        if isinstance(e, ZeroDivisionError):
            print("Caught a ZeroDivisionError!")
        elif isinstance(e, ArithmeticError):
            print("Caught an ArithmeticError!")
        else:
            print("Caught some other exception!")
    
    print("Program continues...")

    在这个脚本中,我们尝试将 10 除以 0,这将引发一个 ZeroDivisionError 异常。然后我们捕获该异常,并使用 isinstance() 函数检查它是否是 ZeroDivisionErrorArithmeticError 的实例。

  4. 在终端中使用以下命令运行脚本:

    python isinstance_exception.py

    这将在终端中打印出 "Caught a ZeroDivisionError!"。

    示例输出:

    Caught a ZeroDivisionError!
    Program continues...

    输出结果表明,isinstance() 函数正确地识别出该异常是 ZeroDivisionError 的实例。由于 ZeroDivisionErrorArithmeticError 的子类,第一个 if 条件满足,因此打印出相应的消息。

    现在,让我们修改脚本以捕获更通用的异常。

  5. isinstance_exception.py 文件修改为以下内容:

    try:
        result = int("abc")
    except Exception as e:
        if isinstance(e, ZeroDivisionError):
            print("Caught a ZeroDivisionError!")
        elif isinstance(e, ValueError):
            print("Caught a ValueError!")
        elif isinstance(e, ArithmeticError):
            print("Caught an ArithmeticError!")
        else:
            print("Caught some other exception!")
    
    print("Program continues...")

    在这个修改后的脚本中,我们尝试将字符串 "abc" 转换为整数,这将引发一个 ValueError 异常。

  6. 再次使用相同的命令运行脚本:

    python isinstance_exception.py

    这将在终端中打印出 "Caught a ValueError!"。

    示例输出:

    Caught a ValueError!
    Program continues...

    这个输出表明,isinstance() 函数可用于区分不同类型的异常,并相应地处理它们。

直接检查异常类

在这一步中,你将学习如何在 except 块中直接检查特定的异常类。与使用 isinstance() 相比,这是一种更直接且通常更清晰的异常处理方式。

当你使用 except ExceptionType as e: 时,你是在告诉 Python 仅捕获类型为 ExceptionType 或其子类的异常。

让我们创建一个 Python 脚本来演示这一点。

  1. 打开你的 VS Code 编辑器。

  2. ~/project 目录下创建一个名为 exception_classes.py 的新文件。

    ~/project/exception_classes.py
  3. exception_classes.py 文件中添加以下代码:

    try:
        result = 10 / 0
    except ZeroDivisionError as e:
        print("Caught a ZeroDivisionError:", e)
    except ArithmeticError as e:
        print("Caught an ArithmeticError:", e)
    except Exception as e:
        print("Caught some other exception:", e)
    
    print("Program continues...")

    在这个脚本中,我们尝试将 10 除以 0,这将引发一个 ZeroDivisionError 异常。我们有三个 except 块:一个用于 ZeroDivisionError,一个用于 ArithmeticError,还有一个用于 Exception

  4. 在终端中使用以下命令运行脚本:

    python exception_classes.py

    这将在终端中打印出 "Caught a ZeroDivisionError: division by zero"。

    示例输出:

    Caught a ZeroDivisionError: division by zero
    Program continues...

    输出结果表明,ZeroDivisionError 被第一个 except 块捕获。

    现在,让我们修改脚本以引发不同的异常。

  5. exception_classes.py 文件修改为以下内容:

    try:
        result = int("abc")
    except ZeroDivisionError as e:
        print("Caught a ZeroDivisionError:", e)
    except ValueError as e:
        print("Caught a ValueError:", e)
    except ArithmeticError as e:
        print("Caught an ArithmeticError:", e)
    except Exception as e:
        print("Caught some other exception:", e)
    
    print("Program continues...")

    在这个修改后的脚本中,我们尝试将字符串 "abc" 转换为整数,这将引发一个 ValueError 异常。

  6. 再次使用相同的命令运行脚本:

    python exception_classes.py

    这将在终端中打印出 "Caught a ValueError: invalid literal for int() with base 10: 'abc'"。

    示例输出:

    Caught a ValueError: invalid literal for int() with base 10: 'abc'
    Program continues...

    这个输出表明,ValueError 被第二个 except 块捕获。

    通过在 except 块中直接指定异常类,你可以清晰、有条理地处理不同类型的异常。对于简单的异常处理,这种方法通常比使用 isinstance() 更受青睐。

总结

在本次实验中,你探索了 Python 中的异常层次结构,这对于有效的错误处理至关重要。你了解到,异常以树状结构组织,其中 BaseException 是基类,而 Exception 是大多数内置异常的超类。

你创建了一个 Python 脚本 exception_hierarchy.py,用于打印从 Exception 类开始的异常层次结构。通过运行该脚本,你观察到了异常的树状结构及其相互关系,从而更好地理解了 Python 中异常的组织方式。