当前位置: 首页 > 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.dtcms.com/a/343381.html

相关文章:

  • 开发者中使用——控制台打印数据
  • 爬虫基础学习-基本原理和GET请求
  • JavaScript 基本语法
  • 智慧城市SaaS平台/市政设施运行监测系统之空气质量监测系统、VOC气体监测系统、污水水质监测系统及环卫车辆定位调度系统架构内容
  • 学习嵌入式之驱动
  • 3.2.6 混凝土基础施工
  • Chrome 内置扩展 vs WebUI:浏览器内核开发中的选择与实践
  • C++入门自学Day16-- STL容器类型总结
  • Git标准化开发流程
  • iOS 应用上架多环境实战,Windows、Linux 与 Mac 的不同路径
  • 详解开源关键信息提取方案PP-ChatOCRv4的设计与实现
  • 哈尔滨云前沿服务器租用类型
  • IoTDB如何解决海量数据存储难题?
  • 多模态大模型研究每日简报【2025-08-21】
  • Python学习-- 数据库和MySQL入门
  • 在线课程|基于SprinBoot+vue的在线课程管理系统(源码+数据库+文档)
  • 华为仓颉语言的函数初步
  • rust语言 (1.88) egui (0.32.1) 学习笔记(逐行注释)(三) 文本标签、按钮、文本框基本使用
  • Rust 入门 模块 (二十二)
  • 意象驱动下的感知与认知信息结构:上古汉语同源词研究视角——基于黄易青《上古汉语同源词意义系统研究》的理论框架
  • Eject配置
  • 常见的端口扫描
  • 从零到一:RAGFlow 本地部署全攻略
  • Python工程师向项目管理转型的深度分析与学习道路规划
  • 容器化与云安全实践:ChartMuseum、私有仓库、云安全、应用部署、生命周期、定时任务、HPA管理案例流程
  • visual studio更改git提交的用户名和邮件
  • LT8712SX,Type-C/DP1.4 /eDP转 DP1.4/HD-DVI2.0 带音频
  • 服务区小汽车违停检测算法创新与实践
  • Android系统框架知识系列(十二):通知中枢 - Notification Manager Service深度解析
  • 工厂模式全解析:简单、方法、抽象工厂详解