在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
。因为 SystemExit
和 KeyboardInterrupt
不被认为是程序"错误",捕获它们会阻止程序正常退出或响应用户中断(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
)-
何时使用:在父类(尤其是抽象基类)中定义一个方法,并强制要求所有子类必须重写(实现)这个方法。它清楚地告诉其他开发者:"这个功能需要你自己去实现"。
-
示例 :
pythonclass 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
时被触发。它主要用于内部自检和调试,检查程序在某个点的状态是否符合预期。它不应该用于验证用户输入。 -
示例 :
pythondef 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
(子类):最常见的语法错误之一,代码缩进不正确。
- 何时发生 :这是你在运行代码之前 就会遇到的错误。Python解释器在解析代码时发现语法不正确。你不能在
三、创建你自己的自定义异常
当内置的异常类型无法精确描述你的应用程序特有的错误时,最佳实践是创建自己的异常类。这能让你的代码更具可读性和可维护性。
自定义异常应该继承自 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
进行内部调试和状态检查。 - 当内置异常不足以描述你的特定问题域时,创建自定义异常,以提高代码的清晰度和健壮性。