1、python 语法
1.1 异常抛出 基本语法
python
raise ValueError("输入格式有误") # 抛出内置异常
raise ValueError() # 使用默认消息 # 抛出异常实例
1.2 异常链: 语法** raise 新异常 from 原始异常 **
-
Python 的 优雅 异常链(Exception Chaining)语法,用于将一个异常与另一个异常关联起来。
- 形成异常的"因果关系"
- 使用 from 保留原始异常
- 异常转换/异常包装(如业务异常包装技术异常)
-
用例展示
python# 优势:不同层看到不同抽象级别的异常! # 按层次定义异常 class UserError(Exception): """用户层异常(直接展示给用户)""" pass class BusinessError(Exception): """业务层异常(记录日志,转换为UserError)""" pass class DataError(Exception): """数据层异常(记录详细日志)""" pass # 数据层 def save_to_database(data): try: db.insert(data) except DatabaseError as e: raise DataError(f"数据库操作失败: {e}") from e # 原始异常会被附加为新异常的 __cause__ # 业务层 def process_user_order(order): try: save_to_database(order) send_notification(order) except DataError as e: log_error(e) # 记录技术细节 raise UserError("订单处理失败,请联系客服") from e # 转换为用户友好的异常 # 用户层(展示给用户) def handle_request(): try: process_user_order(order_data) except UserError as e: show_error_message(str(e)) # 显示友好信息 except Exception as e: log_error(e) show_error_message("系统错误,请稍后重试")
1.3 在函数中声明异常, 通过 raise
python
def divide(a: float, b: float) -> float:
if b == 0:
raise ZeroDivisionError("除数不能为 0")
return a / b
1.4 异常捕获(try-except)
python
# 捕获单个异常
try:
result = 10 / 0
except ZeroDivisionError as e:
print(f"捕获到异常: {e}")
python
# 捕获多个异常
try:
value = int("abc")
except (ValueError, TypeError) as e:
print(f"类型或值错误: {e}")
python
# 捕获所有 Exception类 异常(❌一股脑/未拆分,不推荐)
try:
risky_operation()
except Exception as e:
print(f"发生异常: {e}")
1.5 完整的异常处理结构
python
try:
# 可能抛出异常的代码
result = 10 / 0
except ZeroDivisionError as e:
# 捕获特定异常
print(f"除零错误: {e}")
except ValueError as e:
# 捕获另一种异常
print(f"值错误: {e}")
else:
# 没有异常时执行
print("计算成功")
finally:
# 无论是否异常都执行
print("清理资源")
-
使用 with 打开文件时,即使发生异常,文件也会正确关闭
python# 自动管理资源 # 多个上下文管理器 with open("input.txt", "r") as fin, open("output.txt", "w") as fout: data = fin.read() fout.write(processed_data) -
自定义上下文管理器
pythonfrom contextlib import contextmanager @contextmanager def database_transaction(): """数据库事务上下文管理器""" conn = connect_database() try: yield conn conn.commit() except Exception: conn.rollback() raise finally: conn.close() # 使用 with database_transaction() as conn: conn.execute("INSERT INTO users VALUES (?, ?)", ("Alice", 25))
1.6 异常信息访问
python
try:
raise ValueError("自定义错误消息")
except ValueError as e:
print(f"消息: {e}") # 异常消息
print(f"类型: {type(e).__name__}") # 异常类型
print(f"参数: {e.args}") # 异常参数
print(f"字符串: {str(e)}") # 异常的字符串表示
print(f"Repr: {repr(e)}") # 异常的 repr
1.7 异常传播机制:异常沿着调用栈向上传播
python
def function_c():
raise ValueError("底层错误")
def function_b():
function_c() # 异常从这里开始传播
def function_a():
try:
function_b()
except ValueError as e:
print(f"在 function_a 中捕获: {e}")
function_a() # 输出: 在 function_a 中捕获: 底层错误
# function 是 标准 拼写 !!
2、调试异常
2.1 异常回溯查看
python
import traceback
try:
raise ValueError("测试异常")
except Exception:
traceback.print_exc() # 打印完整的异常回溯
tb_info = traceback.format_exc() # 获取回溯信息
print(f"异常信息:\n{tb_info}")
2.2 使用 logging 记录异常
python
import logging
logger = logging.getLogger(__name__)
try:
risky_operation()
except Exception as e:
logger.error("操作失败", exc_info=True) # 记录异常,包含堆栈信息
logger.exception("操作失败") # 或简写形式
3、项目中的应用总结
3.1 以下设计使得代码健壮、可维护,并且易于调试。
- 定义清晰的异常层次:如 AgentError 作为基类,派生出具体异常
- 在文档中声明异常:使用 Raises: 部分
- 异常传播:底层抛出,上层捕获处理
- 错误日志:使用 logger.error() 记录异常
- 资源清理:在异常发生时清理资源(如 self.messages.pop())
3.2 好的示例
python
# ✅ 捕获特定的异常
try:
data = parse_json(json_string)
except json.JSONDecodeError as e:
logger.error(f"JSON 解析失败: {e}")
handle_error()
python
# ✅ 提供有意义的错误消息
raise ValueError(f"年龄 {age} 无效,必须在 0 到 150 之间")
python
# ✅ 使用异常链保留上下文
try:
load_config()
except IOError as e:
raise RuntimeError("无法加载配置") from e
python
# ✅ 在文档中声明可能抛出的异常
def connect_database(url: str) -> Connection:
"""
连接到数据库
Raises:
ConnectionError: 连接失败时抛出
TimeoutError: 连接超时时抛出
"""
pass
3.3 不恰当示例
python
# ❌ 捕获所有异常但不处理
try:
do_something()
except:
pass # 吞掉所有异常
python
# ❌ 使用裸 except
try:
do_something()
except: # 应该指定异常类型
pass
python
# ❌ 过于宽泛的异常捕获
try:
process_data()
except Exception: # 应该捕获更具体的异常
pass
python
# ❌ 忽略异常信息
try:
risky_operation()
except ValueError:
pass # 没有记录或处理异常信息
4、异常抛出的自定义
- Python支持自定义异常抛出,继承Exception即可,很简单。
4.1 什么场景需要自定义
- 是否需要自定义,主要看场景:
- 小脚本/临时代码 → 用内置异常足够
- 业务应用/框架/库 → 必须定义自定义异常
- 中大型项目 → 建立完整的异常层次体系
- 应该自定义的场景举例
markdown
场景 示例 原因
1. 业务领域错误 订单不存在、库存不足 更清晰表达业务逻辑
2. 需要携带额外信息 错误代码、上下文数据 内置异常无法承载
3. 分层异常处理 控制层、服务层、数据层 不同层处理不同异常
4. 框架/库开发 Web框架、ORM 用户需要区分你的异常
5. 需要特定恢复逻辑 重试、降级 不同异常不同处理方式
4.2 自定义异常继承层次设计示意
python
# 项目中的异常继承层次(参考项目代码)
class AgentError(Exception):
"""Agent 基础异常类, 后续异常类均继承于此,便于管理"""
pass
class ToolExecutionError(AgentError):
"""工具执行异常"""
pass
class ToolValidationError(AgentError):
"""工具参数验证异常"""
pass
class APIError(AgentError):
"""API 调用异常"""
pass
class MaxIterationsExceededError(AgentError):
"""超过最大迭代次数异常"""
pass
4.3 异常抛出 自定义的三种层次
-
简单继承(空异常类)
pythonclass MyError(Exception): """自定义异常基类""" # 继承自Exception pass class SimpleError(MyError): pass -
带构造函数的异常
pythonclass MyError(Exception): """自定义异常基类""" # 继承自Exception pass class DetailedError(MyError): def __init__(self, message: str, code: int): self.message = message self.code = code super().__init__(f"错误 {code}: {message}") -
完整的异常类(带多个方法)
pythonclass MyError(Exception): """自定义异常基类""" # 继承自Exception pass class FullFeaturedError(MyError): def __init__(self, message: str, details: dict): self.message = message self.details = details super().__init__(message) def __str__(self): return f"[{self.message}] 详情: {self.details}" def get_details(self): return self.details
4.4 自定义与使用
-
如需抛出异常时自动处理某些参数,或者给异常绑定特定的变量,则需要替换 pass 为具体代码(通常是 init 方法)。代码示例如下
pythonclass MyError(Exception): """这里需要替换 pass,填写具体逻辑""" def __init__(self, message, error_code, tm): super().__init__(message) # 初始化父类 self.error_code = error_code # 添加自定义属性 self.tm = tm # 封装 raise 逻辑 def fail_fast(message, error_code, tm): # raise 依然存在于这里,只是被藏起来了 raise MyError(message, error_code, tm) # 异常抛出的使用 from datetime import datetime try: ''' 常规函数体,及判定逻辑 可以结合 if 等判定逻辑进行 ''' current_datetime = datetime.now() # 获取当前的日期和时间 formatted_datetime = current_datetime.strftime("%Y-%m-%d %H:%M:%S.%f")[:] # 将datetime对象转换为自定义的字符串格式 raise MyError("系统崩溃了", 500, formatted_datetime) # 或者 fail_fast("系统崩溃了", 500, formatted_datetime) except MyError as e: print(f"错误信息: {e}, 错误代码: {e.error_code}, 错误时间:{e.tm}") -
类的
__str__(self)魔术方法的使用python# 定义自定义异常类 class MyError(Exception): """自定义异常基类""" pass class APIError(MyError): """API 调用异常""" # 构造方法,创建对象时自动调用,初始化对象属性 def __init__(self, message: str, status_code: int = None): super().__init__(message) # 调用父类的 __init__ 方法 self.status_code = status_code # 然后再添加子类特有属性 # Python 一个特殊方法(魔术方法),用于定义对象的字符串表示形式。 # 当需要将对象转换为字符串时,Python 会自动调用 __str__() 方法: def __str__(self): if self.status_code: # 处置灵活 return f"API错误 {self.status_code}: {self.message}" return f"API错误: {self.message}" # 使用自定义异常 try: raise APIError("网络请求失败", status_code=500) except APIError as e: print(e) # API错误 500: 网络请求失败__str__()的 8 种自动触发条件-
条件1: 使用 print() 函数
pythonobj = SomeObject()print(obj) # ← 触发 __str__() -
条件2: 使用 str() 显式转换
pythonobj = SomeObject()str_obj = str(obj) # ← 触发 __str__() -
条件3: f-string 字符串格式化
pythonobj = SomeObject()message = f"结果: {obj}" # ← 触发 __str__() -
条件4: .format() 方法格式化
pythonobj = SomeObject()message = "结果: {}".format(obj) # ← 触发 __str__() -
条件5: % 格式化
pythonobj = SomeObject()message = "结果: %s" % obj # ← 触发 __str__() -
条件6: 字符串拼接(需要显式转换)
pythonobj = SomeObject()message = "结果: " + str(obj) # ← str() 触发 __str__() -
条件7: 日志记录
pythonimport loggingobj = SomeObject()logging.info(f"日志信息: {obj}") # ← 触发 __str__() -
条件8: 返回字符串的函数/方法中
pythondef get_description(obj): return f"描述: {obj}" # ← 触发 __str__()
-
-
捕捉异常进一步响应后,抛出捕获的异常,或向上传递
pythondef main() -> None: """演示异常的使用和处理。""" try: current_datetime = datetime.now() # 模拟业务逻辑 formatted_datetime = current_datetime.isoformat() raise MyError("系统崩溃了", 500, formatted_datetime) # 抛出异常 except MyError as e: print(f"错误代码: {e.error_code}") # 记录错误信息 raise # 向上传递异常 # [或者] raise 新异常 from 原始异常 if __name__ == "__main__": try: main() except MyError as e: print(f"\n最终捕获的错误: {e}") print(f"错误代码: {e.error_code}") print(f"错误时间: {e.timestamp}") -
自定义异常抛出 raise
- 自定义的异常,raise 必须显式出现。因为 Python 只能自动抛出"内置异常",且不知道"业务逻辑",
- 虽然 raise 必须存在,但可以考虑在 类外或者 抛出异常 的类自定义过程中,将之封装在函数中,然后业务代码需要处理抛出时直接做函数调用,以此实现 raise 执行。代码示例如上述 fail_fast() 函数。
4.5 异常抛出自定义的优势
-
概括
- 1、💡 语义清晰
- 2、💡 携带上下文
- 3、💡 精确捕获
- 4、💡 分层处理
- 5、💡 API设计
-
优势1:语义更清晰 📖
python# ❌ 使用内置异常(不清晰) def divide(a, b): if b == 0: raise ValueError("除数不能为零") # 太泛化 # ✅ 使用自定义异常(清晰) class DivisionByZeroError(Exception): """除零错误""" pass def divide(a, b): if b == 0: raise DivisionByZeroError(f"{a} 不能除以 {b}") # 语义明确 # 使用时 try: result = divide(10, 0) except DivisionByZeroError: print("除零错误,请检查输入") # 精确处理 except ValueError: print("其他参数错误") -
优势2:携带额外上下文 🎒
python# 优势:内置异常无法这样携带结构化数据! class ToolExecutionError(Exception): """工具执行异常""" def __init__(self, tool_name: str, error: str): self.tool_name = tool_name # 额外信息:工具名 self.error = error # 额外信息:错误详情 super().__init__(f"工具 '{tool_name}' 执行失败: {error}") # 使用 try: execute_tool("add", {"a": 1, "b": "two"}) # 函数体内需要能够 raise ToolExecutionError,自定义 异常抛出 使用时的前置条件 except ToolExecutionError as e: print(f"工具: {e.tool_name}") # 访问额外信息 print(f"错误: {e.error}") # 可以针对不同工具采取不同恢复策略 if e.tool_name == "add": retry_with_defaults() else: log_error() -
优势3:精确的异常捕获 🎯
python# 定义异常层次 class AgentError(Exception): """Agent基础异常""" pass class ToolError(AgentError): """工具相关异常""" pass class APIError(AgentError): """API相关异常""" pass class ConfigError(AgentError): """配置相关异常""" pass # 精确捕获 try: agent.run() except ToolError: print("工具错误,重试...") except APIError: print("API错误,切换备用服务器...") except ConfigError: print("配置错误,请检查配置文件") except AgentError: print("其他Agent错误")-
对比内置异常:
python# ❌ 所有都可能是ValueError,无法区分 try: agent.run() except ValueError as e: # 这个ValueError可能是工具错误、API错误或配置错误 # 无法精确处理! print("发生错误:", e)
-
-
优势4:分层异常处理 🏗️
python# 优势:不同层看到不同抽象级别的异常! # 按层次定义异常 class UserError(Exception): """用户层异常(直接展示给用户)""" pass class BusinessError(Exception): """业务层异常(记录日志,转换为UserError)""" pass class DataError(Exception): """数据层异常(记录详细日志)""" pass # 数据层 def save_to_database(data): try: db.insert(data) except DatabaseError as e: raise DataError(f"数据库操作失败: {e}") from e # 业务层 def process_user_order(order): try: save_to_database(order) send_notification(order) except DataError as e: log_error(e) # 记录技术细节 raise UserError("订单处理失败,请联系客服") from e # 转换为用户友好的异常 # 用户层(展示给用户) def handle_request(): try: process_user_order(order_data) except UserError as e: show_error_message(str(e)) # 显示友好信息 except Exception as e: log_error(e) show_error_message("系统错误,请稍后重试") -
优势5:框架/库的API设计 📚
python# 优势:库使用者可以精确区分不同错误类型! # 作为一个库作者,提供清晰的异常接口 class DataProcessor: """数据处理库""" class DataFormatError(Exception): """数据格式错误""" pass class ValidationError(Exception): """数据验证错误""" pass class ProcessingError(Exception): """处理过程错误""" pass def process(self, data): if not self._validate_format(data): raise DataFormatError("数据格式不正确") if not self._validate_content(data): raise ValidationError("数据内容不符合要求") if not self._process_data(data): raise ProcessingError("处理过程失败") # 库使用者 processor = DataProcessor() try: processor.process(user_data) except DataFormatError: print("请提供正确格式的数据") except ValidationError: print("数据内容有误") except ProcessingError: print("处理失败,请重试")
4.6 实际对比示例,场景为 一个简单的用户注册功能,
-
方式2更清晰、更易维护
python# ========== 方式1:只用内置异常 ========== def register_user(username, email, password): if not username: raise ValueError("用户名不能为空") if '@' not in email: raise ValueError("邮箱格式错误") if len(password) < 8: raise ValueError("密码长度不足") # ... 注册逻辑 # 使用时 try: register_user("", "invalid", "short") except ValueError as e: # 所有错误都是ValueError,无法区分 print("注册失败:", e) # 如何知道是哪个字段错了?需要解析字符串,很脆弱!python# ========== 方式2:使用自定义异常 ========== class RegistrationError(Exception): """注册异常基类""" pass class UsernameError(RegistrationError): """用户名错误""" pass class EmailError(RegistrationError): """邮箱错误""" pass class PasswordError(RegistrationError): """密码错误""" pass def register_user(username, email, password): if not username: raise UsernameError("用户名不能为空") if '@' not in email: raise EmailError("邮箱格式错误") if len(password) < 8: raise PasswordError("密码长度不足") # ... 注册逻辑 # 使用时 try: register_user("", "invalid", "short") except UsernameError: print("请输入用户名") except EmailError: print("请输入有效的邮箱地址") except PasswordError: print("密码长度至少8位") except RegistrationError: print("注册失败,请检查输入")
5、Python 内置 异常抛出
- Python中的try-except 语句可以捕获任何 继承自BaseException的异常。
5.1 python 内置的异常 层次结构
-
四大系统级异常 (直接继承BaseException)
python异常类型 触发场景 代码设计建议捕获 原因 Exception 所有常规异常的父类 其子类 ✅ 应该 常规程序错误,应该处理 KeyboardInterrupt 用户按 Ctrl+C ⚠️ 谨慎 仅用于清理资源,不要完全忽略 SystemExit sys.exit() 调用 ❌ 不建议 让程序正常退出,除非有特殊需求 GeneratorExit 生成器关闭 ❌ 不需要 生成器内部通常不需要处理 -
简化结构
pythonBaseException ├── SystemExit ├── KeyboardInterrupt ├── GeneratorExit ├── Exception ├── StopIteration ├── ArithmeticError │ ├── ZeroDivisionError │ └── FloatingPointError ├── LookupError │ ├── IndexError │ └── KeyError ├── OSError ├── ValueError ├── TypeError └── RuntimeError -
基本层级架构逻辑为
scssBaseException (顶层) ↓ Exception (中层 - 用户可捕获的异常) ↓ 具体异常类 (底层 - 具体错误类型)
5.2 Exception及其子类 捕获
-
Exception本身不会直接被抛出,但可以用try-except捕获所有继承自Exception的异常。
-
Exception 类捕获 代码示例
python# 捕获 Exception 本身 try: # 任何继承自 Exception 的异常都会被捕获 raise ValueError("值错误") except Exception as e: print(f"捕获到Exception子类: {type(e).__name__}") print(f"消息: {e}")python# 捕获 Exception 的子类 try: raise ValueError("值错误") except ValueError as e: # ValueError 是 Exception 的子类 print(f"捕获到 ValueError: {e}")python# 捕获多个异常类 try: raise TypeError("类型错误") except (ValueError, TypeError, KeyError) as e: print(f"捕获到异常: {e}") -
Exception 子类 by 功能领域的不完全分组:
-
算术域
pythonArithmeticError # 算术错误 ├── FloatingPointError # 精度问题 ├── OverflowError # 范围溢出 └── ZeroDivisionError # 除零错误 -
查找域
pythonLookupError # 查找错误 ├── IndexError # 序列索引错误 └── KeyError # 映射键错误 -
I/O 域
pythonOSError (大量子类) ├── FileNotFoundError # 文件不存在 ├── PermissionError # 权限问题 ├── TimeoutError # 超时 └── ... (更多系统错误) -
ValueError - 值错误
- int("abc") # 无法转换为整数
-
TypeError - 类型错误
- "1" + 2 # 类型不匹配
-
AttributeError - 属性错误
pythonimport math math.sqrt("abc") # math 对象没有 sqrt 属性(实际上有,但参数类型错误) -
ImportError - 导入错误
bashImportError # 模块导入失败 └── ModuleNotFoundError # 找不到模块 -
RuntimeError - 运行时错误
bashRuntimeError # 运行时错误基类 ├── NotImplementedError # 未实现的方法 └── RecursionError # 递归深度超限 -
同类错误归为一组,便于批量捕获(如 except LookupError: 同时处理IndexError和KeyError)。
-
6 补充备用: python 异常 完整层次结构
php
```python
BaseException
├── SystemExit # sys.exit()引发
├── KeyboardInterrupt # 用户中断执行(Ctrl+C)
├── GeneratorExit # 生成器关闭时引发
└── Exception # 所有用户定义异常的基类
├── StopIteration # 迭代器没有更多值
├── StopAsyncIteration # 异步迭代器
├── ArithmeticError # 算术错误基类
│ ├── FloatingPointError # 浮点计算失败
│ ├── OverflowError # 数值溢出
│ └── ZeroDivisionError # 除以零
├── AssertionError # assert语句失败
├── AttributeError # 属性引用失败
├── BufferError # 缓冲区操作失败
├── EOFError # 输入结束
├── ImportError # 模块导入失败
│ └── ModuleNotFoundError # 找不到模块
├── LookupError # 查找失败基类
│ ├── IndexError # 索引超出范围
│ └── KeyError # 字典键不存在
├── MemoryError # 内存不足
├── NameError # 局部/全局名称不存在
│ └── UnboundLocalError # 访问未初始化的局部变量
├── OSError # 操作系统错误基类
│ ├── BlockingIOError # 操作会阻塞
│ ├── ChildProcessError # 子进程操作失败
│ ├── ConnectionError # 连接相关错误基类
│ │ ├── BrokenPipeError # 管道破裂
│ │ ├── ConnectionAbortedError
│ │ ├── ConnectionRefusedError
│ │ └── ConnectionResetError
│ ├── FileExistsError # 文件已存在
│ ├── FileNotFoundError # 文件未找到
│ ├── InterruptedError # 系统调用中断
│ ├── IsADirectoryError # 操作目录而非文件
│ ├── NotADirectoryError # 操作文件而非目录
│ ├── PermissionError # 权限不足
│ └── TimeoutError # 操作超时
├── ReferenceError # 弱引用访问已删除对象
├── RuntimeError # 运行时错误基类
│ ├── NotImplementedError # 未实现的方法
│ └── RecursionError # 递归深度超限
├── SyntaxError # 语法错误
│ └── IndentationError # 缩进错误
│ └── TabError # Tab和空格混用
├── SystemError # 解释器内部错误
├── TypeError # 类型错误
├── ValueError # 值错误
└── Warning # 警告基类
├── DeprecationWarning
├── PendingDeprecationWarning
├── RuntimeWarning
├── SyntaxWarning
├── UserWarning
├── FutureWarning
├── ImportWarning
├── UnicodeWarning
└── BytesWarning
```