Python异常类型介绍

在Python中,我们可以 raise 的 Error(更准确地说是 Exception,异常)类型非常丰富,它们构成了一个清晰的类层次结构。了解这些类型及其用途,对于编写健壮、可读性强的代码至关重要。

NotImplementedError 是一个非常好的例子,它代表了一种特定的"设计意图",而不仅仅是运行时错误。

下面我将为您详细介绍Python中可以 raise 的主要异常类型,并按照它们的逻辑关系和用途进行分类。


一、异常的基本层次结构

所有Python的内置异常都继承自 BaseException 类。这个层次结构非常重要:

复制代码
BaseException
 ├── SystemExit
 ├── KeyboardInterrupt
 └── Exception  <-- 绝大多数异常都继承自这里
      ├── StopIteration
      ├── ArithmeticError
      │    ├── ZeroDivisionError
      │    └── OverflowError
      ├── AssertionError
      ├── AttributeError
      ├── ImportError
      │    └── ModuleNotFoundError
      ├── LookupError
      │    ├── IndexError
      │    └── KeyError
      ├── NameError
      ├── OSError
      │    ├── FileNotFoundError
      │    └── PermissionError
      ├── RuntimeError
      │    └── NotImplementedError  <-- 你提到的在这里
      ├── TypeError
      ├── ValueError
      └── ...等等

一条黄金法则: 在你的 try...except 语句中,通常应该捕获 Exception 或其子类,而不是 BaseException 。因为 SystemExitKeyboardInterrupt 不被认为是程序"错误",捕获它们会阻止程序正常退出或响应用户中断(Ctrl+C)。


二、主要异常类型详解

我将它们分为几类,以便更好地理解它们的用途。

类别1:最常见的运行时错误

这些是你在日常编程中遇到最多的错误。

  • ValueError

    • 何时使用 :当一个函数的参数类型正确,但其不合适时。
    • 示例int('abc')。字符串 'abc' 是一个 str 类型,int() 可以接受 str,但 'abc' 这个值无法被转换成整数。
  • TypeError

    • 何时使用:当对一个对象执行了其类型不支持的操作时。
    • 示例'hello' + 5。你不能将一个字符串和一个整数相加。
  • NameError

    • 何时使用:当尝试使用一个未被定义的变量名时。
    • 示例print(my_undefined_variable)
  • IndexError

    • 何时使用:当试图访问序列(如列表、元组)中一个不存在的索引时。
    • 示例my_list = [1, 2, 3]; print(my_list[5])
  • KeyError

    • 何时使用:当试图访问字典中一个不存在的键时。
    • 示例my_dict = {'a': 1}; print(my_dict['b'])
  • AttributeError

    • 何时使用:当试图访问或赋值一个对象不存在的属性或方法时。
    • 示例my_int = 5; my_int.append(6)。整数没有 append 方法。
类别2:用于程序设计和逻辑控制的异常

这类异常通常由程序员主动 raise,以表明某种设计上的约定或状态。

  • NotImplementedError (继承自 RuntimeError)

    • 何时使用:在父类(尤其是抽象基类)中定义一个方法,并强制要求所有子类必须重写(实现)这个方法。它清楚地告诉其他开发者:"这个功能需要你自己去实现"。

    • 示例

      python 复制代码
      class Shape:
          def get_area(self):
              # 任何继承自Shape的子类都必须实现自己的get_area方法
              raise NotImplementedError("Subclasses must implement this method!")
      
      class Square(Shape):
          def __init__(self, side):
              self.side = side
          
          # 如果不写下面的方法,调用 get_area() 就会报错
          def get_area(self):
              return self.side ** 2
      
      # s = Shape()
      # s.get_area()  # 这会立即引发 NotImplementedError
      sq = Square(5)
      print(sq.get_area()) # 输出 25
  • AssertionError

    • 何时使用 :当 assert 语句的条件为 False 时被触发。它主要用于内部自检和调试,检查程序在某个点的状态是否符合预期。它不应该用于验证用户输入。

    • 示例

      python 复制代码
      def process_data(data):
          assert isinstance(data, list), "Input data must be a list"
          # ... 后续处理 ...
      
      process_data("not a list") # 引发 AssertionError: Input data must be a list
  • RuntimeError

    • 何时使用 :当发生一个不属于任何其他明确类别的错误时。它是一个比较通用的错误,通常表示发生了某些意想不到的外部事件。NotImplementedError 就是它的一个子类。
类别3:与外部环境交互的错误
  • OSError
    • 何时使用:当发生系统相关的错误时,例如I/O操作失败。它是一个广泛的基类。
    • 它的重要子类包括
      • FileNotFoundError : 试图打开一个不存在的文件。open('non_existent_file.txt')
      • PermissionError: 试图以没有权限的方式读写文件。例如,试图写入一个只读文件。
      • ConnectionError : 与网络连接相关的问题,如连接被拒绝 (ConnectionRefusedError)。
      • TimeoutError: 一个操作在指定时间内未能完成。
类别4:算术和导入错误
  • ArithmeticError

    • 何时使用:所有数值计算错误的基类。
    • 子类包括
      • ZeroDivisionError : 除以零。1 / 0
      • OverflowError : 计算结果超出了数字类型能表示的最大范围。import math; math.exp(1000)
  • ImportError

    • 何时使用 :当 import 语句无法找到或加载模块时。
    • ModuleNotFoundError (自 Python 3.6 起):是 ImportError 的一个子类,更明确地表示模块本身未找到。
    • 示例import some_module_that_does_not_exist
类别5:语法错误(特殊情况)
  • SyntaxError
    • 何时发生 :这是你在运行代码之前 就会遇到的错误。Python解释器在解析代码时发现语法不正确。你不能在 try...except 块中捕获它,因为它在代码执行前就失败了。
    • IndentationError (子类):最常见的语法错误之一,代码缩进不正确。

三、创建你自己的自定义异常

当内置的异常类型无法精确描述你的应用程序特有的错误时,最佳实践是创建自己的异常类。这能让你的代码更具可读性和可维护性。

自定义异常应该继承自 Exception 或其某个合适的子类。

示例:假设你在开发一个与外部API交互的应用。

python 复制代码
# 1. 创建一个基础的自定义异常类
class MyAppError(Exception):
    """应用程序所有自定义异常的基类。"""
    pass

# 2. 创建更具体的异常类
class APIResponseError(MyAppError):
    """当API返回一个意外的或错误的响应时引发。"""
    def __init__(self, message, status_code):
        super().__init__(message)
        self.status_code = status_code

# 3. 在代码中使用它
def get_user_data(user_id):
    # response = requests.get(f"https://api.example.com/users/{user_id}")
    # 模拟一个失败的响应
    status_code = 500
    if status_code != 200:
        raise APIResponseError(f"Failed to fetch data, API returned {status_code}", status_code)
    # return response.json()

# 4. 捕获它
try:
    get_user_data(123)
except APIResponseError as e:
    print(f"Error communicating with API: {e}")
    print(f"Status code was: {e.status_code}")
except MyAppError as e:
    print(f"An application-specific error occurred: {e}")

总结

  • 使用 内置异常 来表示通用的编程错误(如 ValueError, TypeError)。
  • 使用 NotImplementedError 来定义接口和抽象方法,强制子类实现。
  • 使用 AssertionError 进行内部调试和状态检查。
  • 当内置异常不足以描述你的特定问题域时,创建自定义异常,以提高代码的清晰度和健壮性。
相关推荐
铲子Zzz14 分钟前
Java使用接口AES进行加密+微信小程序接收解密
java·开发语言·微信小程序
小小小新人1212333 分钟前
C语言 ATM (4)
c语言·开发语言·算法
Two_brushes.41 分钟前
【linux网络】网络编程全流程详解:从套接字基础到 UDP/TCP 通信实战
linux·开发语言·网络·tcp/udp
weixin_4188138743 分钟前
Python-可视化学习笔记
笔记·python·学习
小白学大数据44 分钟前
R语言爬虫实战:如何爬取分页链接并批量保存
开发语言·爬虫·信息可视化·r语言
争不过朝夕,又念着往昔1 小时前
Go语言反射机制详解
开发语言·后端·golang
Azxcc01 小时前
C++异步编程入门
开发语言·c++
Danceful_YJ1 小时前
4.权重衰减(weight decay)
python·深度学习·机器学习
Biaobiaone1 小时前
Java中的生产消费模型解析
java·开发语言
我命由我123451 小时前
前端开发问题:SyntaxError: “undefined“ is not valid JSON
开发语言·前端·javascript·vue.js·json·ecmascript·js