Skip to content

5.1 异常处理基础

引言

小白理解 - 什么是异常?

异常 = 程序运行时出现的错误

类比:开车时遇到的各种意外

意外情况对应的 Python 异常
除以零ZeroDivisionError
文件不存在FileNotFoundError
参数类型错了TypeError
列表下标越界IndexError
网络超时TimeoutError

不处理异常:程序直接崩溃,显示红色错误信息 处理异常:捕获错误,优雅处理或提示用户

try/except/finally

小白理解 - try/except/finally 的结构

python
try:
    # 尝试执行可能出错的代码
    危险操作()

except 某种错误:
    # 如果出错了,执行这里
    处理错误()

finally:
    # 不管有没有出错,最后都执行这里
    清理工作()

类比:拆快递

python
try:
    打开快递盒()        # 可能失败(盒子打不开)

except 打不开Error:
    用剪刀剪开()        # 备用方案

finally:
    把包装纸扔垃圾桶()   # 不管怎样都要收拾
python
def call_api(endpoint: str) -> dict:
    """调用 API(带异常处理)"""
    try:
        # 模拟 API 调用
        if not endpoint:
            raise ValueError("端点不能为空")

        # 实际调用逻辑
        response = {"status": "success", "data": {}}
        return response

    except ValueError as e:
        print(f"参数错误: {e}")
        return {"status": "error", "message": str(e)}

    except Exception as e:
        print(f"未知错误: {e}")
        return {"status": "error", "message": "系统错误"}

    finally:
        print("API 调用完成")

# 使用
result = call_api("")

代码解读

代码含义
try:开始"尝试"执行可能出错的代码
raise ValueError(...)主动抛出一个错误
except ValueError as e:如果是 ValueError,执行这里,e 是错误详情
except Exception as e:其他所有错误都走这里(兜底)
finally:无论如何都会执行(清理资源)

执行顺序

1. 执行 try 里的代码
2. 如果出错 → 跳到对应的 except
3. 如果没出错 → 跳过所有 except
4. 最后执行 finally(必执行)

常见异常类型

小白理解 - 常见错误速查表

错误类型什么时候出现例子
ValueError值不对int("abc")
TypeError类型不对"hello" + 123
KeyError字典键不存在d["不存在的键"]
IndexError列表下标越界[1,2,3][10]
FileNotFoundError文件不存在open("不存在.txt")
ZeroDivisionError除以零1 / 0
AttributeError属性不存在"hello".不存在的方法()
TimeoutError超时网络请求太久

自定义异常

小白理解 - 为什么要自定义异常?

Python 内置的异常(如 ValueError)太"通用"了

问题:你调用 API 出错,但不知道是哪种错误

python
except Exception as e:
    # 是网络错误?密钥错误?还是参数错误?
    # 只能看 e 的文字描述,不方便区分

解决:创建专属的错误类型

python
except APIKeyError:
    # 肯定是密钥问题 → 提示用户检查密钥
except NetworkError:
    # 肯定是网络问题 → 自动重试

自定义异常 = 给错误贴上明确的"标签"

小白理解 - 如何创建自定义异常?

python
class 我的错误(Exception):  # 继承 Exception
    pass                    # 最简单的写法,空着就行

# 使用
raise 我的错误("错误信息")

异常继承树

Exception(Python 内置)
 └── AgentError(你创建的基类)
      ├── ModelNotFoundError(具体错误)
      └── APIKeyError(具体错误)
python
class AgentError(Exception):
    """Agent 基础异常"""
    pass

class ModelNotFoundError(AgentError):
    """模型未找到"""
    pass

class APIKeyError(AgentError):
    """API 密钥错误"""
    pass

def create_agent(model: str, api_key: str):
    if not api_key:
        raise APIKeyError("API 密钥未配置")

    if model not in ["gpt-4", "gpt-3.5-turbo"]:
        raise ModelNotFoundError(f"模型 {model} 不存在")

    return {"model": model, "status": "ready"}

# 使用
try:
    agent = create_agent("gpt-5", "")
except APIKeyError as e:
    print(f"密钥错误: {e}")
except ModelNotFoundError as e:
    print(f"模型错误: {e}")

最佳实践

python
# 1. 创建一个基类异常
class AgentError(Exception):
    pass

# 2. 所有具体异常继承自基类
class APIKeyError(AgentError): pass
class ModelError(AgentError): pass

# 3. 这样可以一次捕获所有 Agent 相关错误
except AgentError as e:  # 捕获所有子类错误
    处理错误()

实战:带重试的 API 调用

小白理解 - 为什么需要重试?

API 调用可能因为网络波动失败,但下一秒可能就好了

没有重试:一次失败就放弃 有重试:失败了再试几次,大大提高成功率

python
import time
from typing import Callable, Any

def retry(max_attempts: int = 3, delay: float = 1.0):
    """重试装饰器"""
    def decorator(func: Callable) -> Callable:
        def wrapper(*args, **kwargs) -> Any:
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt < max_attempts - 1:
                        print(f"第 {attempt + 1} 次失败,{delay}秒后重试...")
                        time.sleep(delay)
                    else:
                        print(f"重试 {max_attempts} 次后仍失败")
                        raise
        return wrapper
    return decorator

@retry(max_attempts=3, delay=1.0)
def call_llm_api(prompt: str) -> str:
    """调用 LLM API(带自动重试)"""
    # 实际 API 调用逻辑
    return f"Response to: {prompt}"

本节小结

概念一句话解释记忆口诀
try尝试执行可能出错的代码try = 试试看
except捕获并处理错误except = 接住错误
finally无论如何都执行finally = 最后收拾
raise主动抛出错误raise = 报警
自定义异常创建专属的错误类型继承 Exception

新手常见问题

Q1:except Exception 和 except 有什么区别?

  • except Exception:捕获所有"普通"异常
  • except::捕获所有异常(包括系统退出),不推荐

Q2:什么时候用 finally?

  • 关闭文件、数据库连接
  • 释放网络资源
  • 任何"必须执行的清理工作"

Q3:应该捕获所有异常吗?

  • 不!只捕获你能处理的
  • 让不能处理的异常继续向上传播

下一节:5.2 日志系统

基于 MIT 许可证发布。内容版权归作者所有。