python学习笔记——异常处理
关于Python 异常处理的 PPT 内容,按「为什么要用异常→怎么用→异常体系」的逻辑完整梳理一遍:
一、为什么要用异常处理?————— 传统错误处理的痛点
1. 就地 / 异地处理的困境
早期的错误处理方式(逻辑判断、返回错误码、全局变量)存在明显缺陷:
逻辑判断(如if y != 0):只能在错误发生的现场处理,无法跨函数传递,多层调用时处理逻辑会散落在各处。
返回错误码:函数需要同时返回正常结果和错误标识,调用者必须额外判断,多层调用时需要逐层检查,代码冗余且容易遗漏。
全局变量标识:状态传递依赖全局变量,嵌套关系复杂时传递繁琐,还会破坏代码的封装性。
2. 异常处理的核心优势:分离 “发现错误” 和 “处理错误”
异常处理实现了两者的解耦:
发现错误:在底层函数中,当出现预期外的状态时,直接用raise抛出异常,无需考虑如何处理。
处理错误:在调用链的任意上层,用try-except捕获并处理异常,无需修改底层函数的逻辑。
以除法运算为例:
def funcDiv(x, y):
if y != 0:
return x / y
else:
raise Exception("div-zero") # 仅负责抛出,不处理
即使经过多层函数调用,异常也会沿着调用链向上传递,直到被捕获,无需逐层传递错误标识。
二、Python 异常的核心机制
1. 异常的自动发现与手动抛出
自动发现:Python 解释器会自动检测运行时错误(如除零、类型错误),并抛出对应的内置异常(如ZeroDivisionError)。
手动抛出:开发者可以用raise
2. 异常对象与分类
所有 Python 内置异常都是Exception类的派生类,不同异常类型对应不同的错误场景,方便针对性处理:
| 异常类型 | 触发场景 |
|---|---|
| NameError | 变量名未定义 |
| TypeError | 类型不匹配(如字符串和数字相加) |
| ValueError | 值无效(如int("abc")) |
| AttributeError | 对象没有指定属性(如list.foo) |
| ZeroDivisionError | 除零错误 |
| IndexError | 索引超出序列范围 |
| KeyError | 字典中不存在指定键 |
异常对象包含公共属性和方法,如__str__(错误信息)、with_traceback()(追踪调用栈),调试时会自动打印完整的调用链,快速定位错误位置。
三、try-except异常处理语法
1. 基本结构
try:
# 可能抛出异常的代码
result = funcDiv(1, 0)
except ExceptionClass as e:
# 捕获并处理指定类型的异常
print(f"捕获到异常:{e}")
try块:声明可能出现异常的代码段。
except块:按异常类型捕获错误,可绑定异常对象(as e)获取错误信息。
2. try-except 基础语法
1. 捕获异常的两种方式
捕获任意异常:使用空的 except 子句,会捕获所有类型的异常。
try:
x = data_processing(0, 1, compute)
except:
print("handling div-zero error!")
捕获特定类型异常:在 except 后指定异常类,仅捕获对应类型的错误,便于针对性处理。
except ZeroDivisionError:
print("handling div-zero error!")
2. 多异常处理
可以通过多个 except 子句,为不同类型的异常定义不同的处理逻辑:
try:
x = data_processing("1", 1, compute)
except ZeroDivisionError:
print("handling div-zero error!")
except TypeError:
print("handling type error!")
3. 异常处理的位置
异常可以在调用链的任意位置处理:
在底层函数内部捕获(如 randfunc 中处理除零错误)
在上层调用处捕获其他未处理的异常,实现分层错误处理
try-except 扩展语法
1. else 子句
当 try 块中没有发生异常时,会执行 else 块的代码:
try:
result = func(1, 2)
except ZeroDivisionError:
print("除零错误")
else:
print("计算成功,结果为:", result)
2. finally 子句
无论是否发生异常,finally 块的代码都会执行,常用于资源清理(如关闭文件、释放连接):
try:
file = open("data.txt", "r")
except FileNotFoundError:
print("文件不存在")
finally:
file.close() # 确保文件一定会被关闭
- 异常的重新抛出
在 except 块中,可以用 raise 语句将捕获的异常再次抛出,交由外层调用者处理:
try:
func(1, 0)
except ZeroDivisionError as e:
# 记录日志后重新抛出
raise Exception("计算错误") from e
四、Python 内置异常体系
Python 提供了丰富的内置异常类型,覆盖了绝大多数运行时错误场景:
值 / 类型相关:NameError(变量未定义)、TypeError(类型不匹配)、ValueError(值无效)、AttributeError(对象无指定属性)
文件 / IO 相关:FileNotFoundError(文件不存在)、FileExistsError(文件已存在)
断言错误:AssertionError,当 assert 语句的条件为 False 时触发,用于将逻辑错误转为异常
语法错误:SyntaxError,解释器执行前会抛出的语法错误
五、用户自定义异常
可以通过继承 Exception 类或其派生类,定义自己的异常类型:
1. 基础自定义异常
class ZeroExc(Exception):
pass
2. 带附加信息的自定义异常
可以在异常类中存储额外信息,方便调试和处理:
class DividingError(Exception):
def __init__(self, str, x, y):
self.x = x
self.y = y
super().__init__(str)
3. 使用自定义异常
try:
x = data_processing(0, 1, compute)
except DividingError as de:
print(de)
print(f"dividing {de.x} by {de.y}")
except:
print("unhandled error!")
六、上下文管理器(with 语句)
1. 核心作用
上下文管理器用于管理资源的获取与释放,确保代码块执行前后的操作一定会被执行(如文件的打开与关闭),简化了 try-finally 的资源清理逻辑。
2. 工作原理
上下文管理器通过两个魔术方法实现:
enter:在进入 with 代码块时执行,负责资源初始化
exit:在退出 with 代码块时执行,负责资源清理(无论是否发生异常)
3. 典型示例:文件操作
with open('foo.txt') as f:
# 执行文件操作
content = f.read()
# 离开 with 块时,文件会自动关闭,无需手动调用 f.close()
4. 底层实现伪代码
manager = (EXPRESSION)
enter = type(manager).__enter__
exit = type(manager).__exit__
value = enter(manager)
try:
TARGET = value
SUITE # with 代码块
except:
hit_except = True
if not exit(manager, *sys.exc_info()):
raise
finally:
if not hit_except:
exit(manager, None, None, None)
上下文管理器则通过 with 语句简化了资源管理,确保代码执行前后的操作一定会被执行。

浙公网安备 33010602011771号