当前位置: 首页 > news >正文

2025-08-21 Python进阶4——错误和异常

文章目录

  • 1 错误的两种类型
    • 1.1 语法错误(解析错误)
    • 1.2 异常
  • 2 异常的处理
    • 2.1 基本语法
    • 2.2 处理多种异常
    • 2.3 异常的继承关系
    • 2.4 获取异常信息
    • 2.5 else 子句
    • 2.6 异常在函数中的传播
  • 3 主动触发异常
    • 3.1 基本用法
    • 3.2 重新引发异常
  • 4 异常链
    • 4.1 隐式异常链
    • 4.2 显式异常链
    • 4.3 禁用异常链
  • 5 用户自定义异常
    • 5.1 定义自定义异常
    • 5.2 使用自定义异常
  • 6 清理操作
    • 6.1 finally 子句
    • 6.2 with 语句
  • 7 处理多个不相关的异常
    • 7.1 引发异常组
    • 7.2 捕获异常组
  • 8 为异常添加注释
  • 9 总结

1 错误的两种类型

Python 中的错误主要分为两类:语法错误异常

1.1 语法错误(解析错误)

语法错误是最常见的错误类型,通常是由于代码不符合 Python 的语法规则导致的。当解析器检测到语法错误时,会指出错误位置并终止程序。

示例

# 缺少冒号
while True print('Hello world')

错误信息

  File "<stdin>", line 1while True print('Hello world')^^^^^
SyntaxError: invalid syntax

说明:错误信息中的箭头指向解析器检测到错误的位置,但实际需要修复的可能是其附近的语法(如上例中 while True 后缺少冒号)。

1.2 异常

即使代码语法正确,在执行过程中也可能发生错误,这类错误称为异常。异常不会导致解析器崩溃,但如果不处理,会导致程序终止并显示错误信息。

常见异常示例

# 除以零
10 * (1/0)  # ZeroDivisionError: division by zero# 使用未定义的变量
4 + spam*3  # NameError: name 'spam' is not defined# 类型不匹配
'2' + 2  # TypeError: can only concatenate str (not "int") to str

异常信息解读

  • 错误信息最后一行显示异常类型(如 ZeroDivisionError)和详细描述
  • 前面的内容是堆栈回溯,显示异常发生的位置和调用链

2 异常的处理

Python 提供 try...except 语句来捕获和处理异常,使程序在遇到异常时能够继续执行。

2.1 基本语法

try:# 可能引发异常的代码risky_operation()
except ExceptionType:# 处理特定类型异常的代码handle_exception()

示例:处理用户输入非数字的情况

while True:try:x = int(input("请输入一个数字: "))breakexcept ValueError:print("无效的数字,请重新输入...")

工作原理

  1. 执行 try 子句中的代码
  2. 如果没有异常,跳过 except 子句,继续执行后续代码
  3. 如果发生异常,检查异常类型是否与 except 后指定的类型匹配
  4. 若匹配,执行 except 子句中的处理代码,然后继续执行后续代码
  5. 若不匹配,异常会传递到外层代码,若没有其他处理器,则程序终止

2.2 处理多种异常

可以使用多个 except 子句处理不同类型的异常,也可以在一个 except 中处理多种异常。

try:# 可能引发多种异常的代码f = open('myfile.txt')s = f.readline()i = int(s.strip())
except OSError as err:print(f"操作系统错误: {err}")
except ValueError:print("无法将数据转换为整数")
except Exception as err:print(f"意外错误: {err=}, {type(err)=}")raise  # 重新引发异常,让上层处理

捕获多种异常的方式

# 方式1:多个 except 子句
except RuntimeError:handle_runtime_error()
except TypeError:handle_type_error()# 方式2:元组指定多个异常
except (RuntimeError, TypeError, NameError):handle_these_errors()

2.3 异常的继承关系

异常处理遵循继承关系:如果一个 except 子句指定了基类,则它会捕获该基类及其派生类的所有异常。

class B(Exception):passclass C(B):passclass D(C):passfor cls in [B, C, D]:try:raise cls()except D:print("D")except C:print("C")except B:print("B")
# 输出: B C D

注意:如果将 except B 放在最前面,将会捕获所有 BCD 类型的异常,因为它们都是 B 的派生类。

2.4 获取异常信息

可以通过 as 关键字将异常实例绑定到变量,从而获取详细信息。

try:raise Exception('spam', 'eggs')
except Exception as inst:print(type(inst))    # 异常类型: <class 'Exception'>print(inst.args)     # 异常参数: ('spam', 'eggs')print(inst)          # 异常字符串表示: ('spam', 'eggs')x, y = inst.args     # 解包参数print(f'x = {x}, y = {y}')

2.5 else 子句

try...except 可以包含 else 子句,用于执行那些在 try 子句没有引发异常时才需要执行的代码。

import sysfor arg in sys.argv[1:]:try:f = open(arg, 'r')except OSError:print(f'无法打开文件: {arg}')else:# 只有文件成功打开时才执行print(f'{arg}{len(f.readlines())} 行')f.close()

优势:将不需要异常保护的代码放在 else 中,避免意外捕获其他代码引发的异常。

2.6 异常在函数中的传播

异常处理程序不仅处理 try 子句中直接发生的异常,还处理 try 子句中调用的函数(包括间接调用)中发生的异常。

def this_fails():x = 1/0try:this_fails()
except ZeroDivisionError as err:print(f'处理运行时错误: {err}')
# 输出: 处理运行时错误: division by zero

3 主动触发异常

使用 raise 语句可以主动触发指定的异常。

3.1 基本用法

# 触发异常
raise NameError('这是一个自定义错误信息')# 触发异常类(会隐式创建实例)
raise ValueError  # 等价于 raise ValueError()

3.2 重新引发异常

在异常处理后,可以使用不带参数的 raise 重新引发当前异常,让上层代码继续处理。

try:raise NameError('HiThere')
except NameError:print('一个异常被捕获!')raise  # 重新引发异常

输出

一个异常被捕获!
Traceback (most recent call last):File "<stdin>", line 2, in <module>
NameError: HiThere

4 异常链

当一个异常导致另一个异常时,可以使用异常链来表示这种关系,使错误信息更清晰。

4.1 隐式异常链

当在 except 子句中引发新异常时,Python 会自动创建异常链。

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

错误信息

Traceback (most recent call last):File "<stdin>", line 2, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'database.sqlite'During handling of the above exception, another exception occurred:Traceback (most recent call last):File "<stdin>", line 4, in <module>
RuntimeError: 无法处理错误

4.2 显式异常链

使用 from 子句可以显式指定异常之间的因果关系。

def func():raise ConnectionErrortry:func()
except ConnectionError as exc:# 显式指定异常原因raise RuntimeError('无法打开数据库') from exc

错误信息

The above exception was the direct cause of the following exception:Traceback (most recent call last):File "<stdin>", line 4, in <module>
RuntimeError: 无法打开数据库

4.3 禁用异常链

使用 from None 可以禁用自动异常链。

try:open('database.sqlite')
except OSError:raise RuntimeError from None

5 用户自定义异常

可以通过创建新的异常类来定义自己的异常类型,通常继承自 Exception 类。

5.1 定义自定义异常

class CustomError(Exception):"""自定义异常类"""passclass ValueTooSmallError(CustomError):"""值太小的异常"""def __init__(self, value, min_value):self.value = valueself.min_value = min_valueself.message = f"值 {value} 太小,最小值为 {min_value}"super().__init__(self.message)

5.2 使用自定义异常

def check_value(n):if n < 10:raise ValueTooSmallError(n, 10)return Truetry:check_value(5)
except ValueTooSmallError as e:print(f"捕获到自定义异常: {e}")

最佳实践

  • 异常类名通常以 “Error” 结尾
  • 保持异常类简单,主要用于传递错误信息
  • 适当组织异常的继承层次,便于分类处理

6 清理操作

在有些情况下,无论是否发生异常,都需要执行一些清理操作(如释放资源)。Python 提供了 finally 子句和 with 语句来处理这种情况。

6.1 finally 子句

finally 子句中的代码无论是否发生异常都会执行。

try:raise KeyboardInterrupt
finally:print('程序即将退出!')

输出

程序即将退出!
Traceback (most recent call last):File "<stdin>", line 2, in <module>
KeyboardInterrupt

复杂场景中的行为

  • 如果 try 子句发生异常且未被处理,finally 执行后异常会被重新引发
  • exceptelse 子句中发生的异常,会在 finally 执行后重新引发
  • 如果 finally 包含 breakcontinuereturn,异常不会被重新引发
  • finally 中的 return 会覆盖 tryexcept 中的 return

示例

def divide(x, y):try:result = x / yexcept ZeroDivisionError:print("除以零!")else:print("结果是", result)finally:print("执行清理操作")divide(2, 1)
# 输出:
# 结果是 2.0
# 执行清理操作divide(2, 0)
# 输出:
# 除以零!
# 执行清理操作

6.2 with 语句

with 语句用于定义一个资源的使用范围,确保资源在使用后被正确清理(如文件自动关闭)。

# 传统方式
f = open("myfile.txt")
try:for line in f:print(line, end="")
finally:f.close()# 使用 with 语句(更简洁)
with open("myfile.txt") as f:for line in f:print(line, end="")

工作原理:支持 with 语句的对象实现了上下文管理协议(__enter____exit__ 方法),__exit__ 方法会负责资源清理。

7 处理多个不相关的异常

Python 3.11 引入了 ExceptionGroup,用于同时处理多个不相关的异常,这在并发场景中特别有用。

7.1 引发异常组

def f():excs = [OSError('错误 1'), SystemError('错误 2')]raise ExceptionGroup('发生多个问题', excs)f()

输出

  + Exception Group Traceback (most recent call last):|   File "<stdin>", line 1, in <module>|     f()|   File "<stdin>", line 3, in f|     raise ExceptionGroup('发生多个问题', excs)| ExceptionGroup: 发生多个问题 (2 sub-exceptions)+-+---------------- 1 ----------------| OSError: 错误 1+---------------- 2 ----------------| SystemError: 错误 2+------------------------------------

7.2 捕获异常组

使用 except* 可以选择性地处理异常组中特定类型的异常。

def f():raise ExceptionGroup("group1",[OSError(1),SystemError(2),ExceptionGroup("group2",[OSError(3), RecursionError(4)])])try:f()
except* OSError as e:print("处理所有 OSError 异常")
except* SystemError as e:print("处理所有 SystemError 异常")

输出

处理所有 OSError 异常
处理所有 SystemError 异常+ Exception Group Traceback (most recent call last):|   ...| ExceptionGroup: group1 (1 sub-exception)+-+---------------- 1 ----------------| ExceptionGroup: group2 (1 sub-exception)+-+---------------- 1 ----------------| RecursionError: 4+------------------------------------

8 为异常添加注释

使用 add_note() 方法可以为异常添加额外的上下文信息,帮助调试。

try:raise TypeError('类型错误')
except Exception as e:e.add_note('补充信息 1')e.add_note('补充信息 2')raise

输出

Traceback (most recent call last):File "<stdin>", line 2, in <module>
TypeError: 类型错误
补充信息 1
补充信息 2

应用场景:为异常添加发生时的上下文(如迭代次数、处理的数据等),使错误信息更完整。

9 总结

  • Python 错误分为语法错误和异常两类
  • 使用 try...except 捕获和处理异常,可指定处理特定类型的异常
  • raise 语句用于主动触发异常
  • 异常链用于表示异常之间的因果关系
  • 可以定义自定义异常类来表示特定错误
  • finally 子句和 with 语句用于确保清理操作的执行
  • ExceptionGroup 用于处理多个不相关的异常
  • add_note() 可给异常添加额外信息

掌握异常处理机制能够编写更健壮、更易维护的程序,特别是在处理用户输入、文件操作、网络请求等可能出现意外情况的场景中尤为重要。

http://www.xdnf.cn/news/1341235.html

相关文章:

  • 用 Python 写的自动化测试 WPF 程序的一个案例
  • 【GaussDB】使用gdb定位GaussDB编译package报错
  • Spring Boot整合Amazon SNS实战:邮件订阅通知系统开发
  • 第三阶段数据库-6:sql中函数,多表查询,运算符,索引,约束
  • 我从零开始学微积分(2)- 函数与图形
  • 与森马品牌代言人王安宇专注日常力量,再启新常服故事
  • Qt二维码生成器项目开发教程 - 从零开始构建专业级QR码生成工具
  • 精算中的提升曲线(Lift Curve)与机器学习中的差别
  • Design Compiler:逻辑库名与逻辑库文件名及其指定方式
  • 交易高光时刻-01
  • langgraph快速搭建agent后端和react前端
  • springboot 启动后get请求任意接口地址会跳到登录页
  • 【TrOCR】模型预训练权重各个文件解读
  • 【Java集合】List,Map,Set-详细讲解
  • ODDR实现多bit单边沿采样数据转为多bit双沿采样数据
  • 效率跃迁 ,亚数TrustAsia 加速证书管理迈向 CaaS 新阶段
  • 意象驱动的深层语义:感知认知统一对自然语言处理与知识图谱的影响
  • 活性数据处理与标准化
  • 在互联网大厂的Java面试:谢飞机的搞笑历险记
  • 学习 k 均值聚类算法的心得
  • 2025-08-21 Python进阶8——命名空间作用域
  • gRPC 与 HTTP 性能对比分析
  • 微算法科技(NASDAQ:MLGO)构建去中性化区块链预言机,实现跨链信息互通
  • 使用 X11 转发服务器界面
  • 整体设计 之定稿 “凝聚式中心点”原型 --整除:智能合约和DBMS的在表层挂接 能/所 依据的深层套接 之2
  • 迅为R3568开发板OpeHarmony学习开发手册-配置远程访问环境
  • Typescript入门-函数讲解
  • 面试后的跟进策略:如何提高录用几率并留下专业印象
  • Shell 变量全解析:从基础到高级技巧
  • C语言基础习题——01