飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 Python简介

    • Python是什么
    • 安装与运行
    • 交互式解释器
    • 注释与编码规范
  • 第2章 变量与数据类型

    • 变量与对象
    • 整数 int
    • 浮点数 float
    • 复数 complex
    • 布尔值 bool
    • 字符串 str
    • 空值 None
    • 类型转换
  • 第3章 运算符与表达式

    • 算术运算符
    • 比较运算符
    • 赋值运算符
    • 逻辑运算符
    • 位运算符
    • 身份与成员运算符
    • 海象运算符
    • 运算符优先级
  • 第4章 流程控制

    • if 语句
    • if-else 语句
    • if-elif-else 语句
    • match-case 语句
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 函数
    • break 与 continue
    • 循环的 else 子句
    • pass 语句
  • 第5章 数据结构

    • 列表创建与索引
    • 列表方法
    • 列表推导式
    • 元组
    • 序列解包
    • 集合
    • 字典创建与访问
    • 字典方法
    • 字典推导式
    • range 对象
  • 第6章 函数

    • 定义函数
    • 位置参数与关键字参数
    • 默认参数
    • 可变参数
    • 解包实参
    • 函数返回值
    • lambda 表达式
    • 文档字符串与注解
    • 作用域与命名空间
    • global 与 nonlocal
  • 第7章 模块与包

    • 模块导入
    • 模块搜索路径
    • 包与相对导入
    • 标准库概览
  • 第8章 文件与输入输出

    • 文件读写
    • 上下文管理器
    • 字符串格式化
    • JSON 与 CSV
  • 第9章 面向对象

    • 类与对象
    • 方法
    • 实例变量与类变量
    • 私有变量
    • 继承
    • 多重继承
    • 魔术方法
    • 属性装饰器
    • 数据类 dataclass
  • 第10章 异常处理

    • 语法错误与异常
    • try-except
    • 异常链与 raise
    • 清理操作
    • 自定义异常
  • 第11章 迭代器与生成器

    • 迭代器协议
    • 生成器
    • 生成器表达式
    • 迭代工具
  • 第12章 高级特性

    • 装饰器
    • 函数式编程
  • 第13章 工程实践

    • 测试与调试
    • 代码质量
    • 虚拟环境

异常链与 raise

raise 语句是 Python 中主动触发异常的唯一方式。它不仅可以抛出新的异常,还能通过异常链机制保留原始错误上下文,让调试者看清问题的完整来龙去脉。

raise 基本语法

raise 的唯一参数必须是一个异常实例或异常类(派生自 BaseException)。如果传入的是类,Python 会自动调用其无参构造函数来创建实例。

# 抛出异常实例
raise NameError("HiThere")
# Traceback: NameError: HiThere

# 传入类,自动实例化
raise ValueError
# 等价于 raise ValueError()

# 带参数的自动实例化
raise RuntimeError("配置加载失败")

异常实例的构造参数会被存入 .args 属性,并在 Traceback 的最后部分显示为错误详情。因此,构造异常时传入的字符串应当清晰描述问题。

def load_file(path):
    if not path.endswith(".txt"):
        raise ValueError(f"只支持 .txt 文件,收到:{path}")
    with open(path) as f:
        return f.read()

load_file("data.csv")  # ValueError: 只支持 .txt 文件,收到:data.csv

重新抛出异常

在 except 子句中,单独的 raise 语句(不带参数)会重新抛出当前正在处理的异常。这在需要记录或清理后再把异常交给上层处理时非常有用。

try:
    raise NameError("HiThere")
except NameError:
    print("异常掠过,记录日志...")
    raise  # 重新抛出同一个 NameError

重新抛出保留了原始的 Traceback,不会丢失任何上下文信息。这与 raise NameError("HiThere") 不同——后者会创建一个新的异常实例和新的抛出点。

try:
    risky_call()
except Exception as e:
    logger.error(f"操作失败:{e}")
    raise  # 原样抛出,调用者看到原始 Traceback

raise from:显式异常链

当一个异常是另一个异常的直接后果时,可以使用 raise ... from 建立显式的因果关系。这在异常转换场景中尤为重要——例如将低层异常包装为高层业务异常,同时保留原始原因。

def func():
    raise ConnectionError("数据库连接超时")

try:
    func()
except ConnectionError as exc:
    raise RuntimeError("无法初始化服务") from exc

Traceback 会显示两个异常,并用 The above exception was the direct cause of the following exception: 标明因果关系:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in func
ConnectionError: 数据库连接超时

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: 无法初始化服务

异常链信息存储在异常实例的 __cause__ 属性中。通过 raise ... from 转换异常,调用者既能捕获高层的 RuntimeError 做业务处理,也能通过 __cause__ 访问底层的 ConnectionError 做技术诊断。

try:
    raise RuntimeError("服务启动失败") from ConnectionError("端口被占用")
except RuntimeError as e:
    print(e)           # 服务启动失败
    print(e.__cause__) # 端口被占用
    print(type(e.__cause__))  # <class 'ConnectionError'>

隐式异常链

如果在 except 子句处理异常的过程中又触发了新的异常,Python 会自动将两个异常关联起来,形成隐式异常链。Traceback 中用 During handling of the above exception, another exception occurred: 连接。

try:
    open("database.sqlite")
except OSError:
    raise RuntimeError("无法处理错误")

这里 RuntimeError 不是 OSError 的直接后果,而是在处理 OSError 的过程中意外发生的。隐式链通过 __context__ 属性存储前一个异常。

from None:禁用异常链

有时你不希望暴露底层异常的细节,可以使用 from None 来禁用自动异常链,让 Traceback 只显示新的异常。

try:
    open("secret.key")
except OSError:
    raise RuntimeError("认证失败") from None

输出将只有 RuntimeError: 认证失败,不会提及文件不存在的细节。这在安全敏感场景中有用,但日常调试中应谨慎使用,避免隐藏真正的问题根源。

assert 语句

assert 是一种特殊的异常触发机制,用于检查程序内部的不变式。它只在调试模式下有效——当 Python 以优化模式运行(python -O)时,所有 assert 语句会被移除。

def divide(a, b):
    assert b != 0, "除数不能为零"
    return a / b

divide(10, 0)  # AssertionError: 除数不能为零

assert 的语法是 assert condition [, message]。如果 condition 为假,则抛出 AssertionError,可选的 message 作为异常参数。

assert 适用于检查"绝不应该发生"的编程错误,而不是处理外部输入或运行时环境的不确定性。用户输入错误应当用 if/raise 处理,只有程序逻辑自身的矛盾才适合用 assert。

# ✅ 正确使用:检查内部不变式
def process_list(items):
    assert isinstance(items, list), "内部错误:期望 list 类型"
    assert len(items) > 0, "内部错误:列表不应为空"
    return sum(items) / len(items)

# ❌ 错误使用:用 assert 做输入校验
def user_login(password):
    assert len(password) >= 8, "密码太短"  # 危险!优化模式下被移除
    # 应当用:
    if len(password) < 8:
        raise ValueError("密码至少需要8个字符")

异常链的遍历

Python 3.12 中,可以通过 __cause__(显式链)和 __context__(隐式链)属性遍历完整的异常链,实现自定义的错误报告逻辑。

def print_exception_chain(exc):
    depth = 0
    while exc:
        indent = "  " * depth
        print(f"{indent}{type(exc).__name__}: {exc}")
        exc = exc.__cause__ or exc.__context__
        depth += 1

try:
    try:
        int("not a number")
    except ValueError as e:
        raise RuntimeError("数据转换失败") from e
except RuntimeError as e:
    print_exception_chain(e)
# ValueError: invalid literal for int() with base 10: 'not a number'
#   RuntimeError: 数据转换失败
上一页
try-except
下一页
清理操作