Python 程序运行中出现的报错、崩溃、逻辑非法统称为异常。如果不做异常捕获,程序会直接终止、中断服务。异常处理是项目健壮性、容错性、线上稳定性的核心保障,是工程开发必备核心能力。
本文全覆盖指定重难点:完整 try-except-else-finally 结构、主动抛出 raise、自定义异常、异常链 raise…from。全程搭配可运行代码、底层执行逻辑、工程踩坑点、高频面试真题,体系化掌握 Python 异常高阶用法。
一、Python异常基础认知
异常是程序运行时发生的非语法错误,是逻辑或运行环境异常,例如:除零、下标越界、类型不匹配、文件不存在、网络超时等。
Python 异常处理核心目标:捕获错误、记录日志、优雅兜底、避免程序崩溃。
常见内置异常:
1、ZeroDivisionError:除零异常;
2、IndexError:列表下标越界;
3、KeyError:字典键不存在;
4、TypeError:类型错误;
5、FileNotFoundError:文件不存在;
6、AttributeError:属性不存在。
二、完整异常捕获结构:try-except-else-finally
Python 提供四段式完整异常结构,四个模块各司其职,是工程标准写法,区别于简单 try-except 最简写法。
执行顺序规则:
1、try:尝试执行核心业务代码;
2、出错 → 执行 except;
3、无错 → 执行 else;
4、无论是否出错,最终必执行 finally。
2.1 完整结构实战演示
def calc_div(a, b):
try:
# 核心业务代码:可能出错的逻辑
res = a / b
except ZeroDivisionError:
# 捕获指定异常:除零错误
print("捕获异常:除数不能为0")
return None
else:
# 无异常时执行
print("计算正常完成,无异常")
return res
finally:
# 无论成败,必执行:常用于资源释放
print("执行finally:收尾、释放资源、关闭连接")
# 测试报错场景
calc_div(10, 0)
print("-" * 30)
# 测试正常场景
calc_div(10, 2)
2.2 多异常捕获与通用异常捕获
工程中可精准捕获指定异常(推荐),也可使用 Exception 捕获所有常规异常,禁止裸写 except 不写类型。
def test_multi_error():
try:
lst = [1, 2, 3]
print(lst[10]) # 触发IndexError
# print(1 / 0) # 触发ZeroDivisionError
except IndexError:
print("捕获:下标越界异常")
except ZeroDivisionError:
print("捕获:除零异常")
except Exception as e:
# 兜底捕获所有常规异常
print(f"捕获未知异常:{type(e)},信息:{e}")
test_multi_error()
工程规范:优先精准捕获具体异常,最后用 Exception 兜底,方便问题定位,避免吞异常。
2.3 核心坑点(面试高频)
1、else 不参与异常保护:else 中代码报错不会被前面 except 捕获;
2、finally 优先级最高:即使 try/except 中有 return,依然会先执行 finally 再返回;
3、禁止滥用 except:捕获过宽会隐藏 bug,导致线上问题无法排查。
三、主动抛出异常 raise
Python 不仅可以捕获异常,还可以通过 raise 主动抛出异常。用于参数校验、权限拦截、业务逻辑不合法场景,主动终止非法流程、抛出规范错误。
3.1 raise 基础用法
def get_age(age):
if not isinstance(age, int):
raise TypeError("年龄必须是整数类型")
if age < 0 or age > 150:
raise ValueError(f"年龄非法:{age},范围必须0-150")
print(f"合法年龄:{age}")
# get_age("十八") # 主动抛TypeError
get_age(200) # 主动抛ValueError
3.2 单纯 raise 用法(复刻原异常)
单独写 raise 不跟异常类型,用于捕获异常后二次上抛,保留原始异常堆栈。
def read_num():
try:
num = 1 / 0
except ZeroDivisionError:
print("记录日志:出现除零异常")
# 复刻抛出原始异常,不修改异常信息
raise
read_num()
四、自定义异常(工程开发核心)
内置异常无法满足复杂业务场景,Python 支持自定义业务异常,统一项目错误码、错误信息、异常层级,是中大型项目标准规范。
自定义异常规则:继承内置 Exception 类。
4.1 基础自定义异常
# 自定义业务异常基类
class BusinessError(Exception):
"""项目业务异常总基类"""
pass
# 细分具体业务异常
class ParamInvalidError(BusinessError):
"""参数非法异常"""
pass
class UserNotExistError(BusinessError):
"""用户不存在异常"""
pass
# 业务中主动抛出自定义异常
def login(username):
if not username:
raise ParamInvalidError("用户名不能为空")
if username != "admin":
raise UserNotExistError(f"用户{username}不存在")
print("登录成功")
login("")
4.2 带错误码的高阶自定义异常
class ApiError(Exception):
"""统一接口异常"""
def __init__(self, code, msg):
self.code = code
self.msg = msg
super().__init__(f"[{code}] {msg}")
# 抛出自定义带码异常
def query_order(order_id):
if not order_id:
raise ApiError(code=40001, msg="订单ID不能为空")
print("查询订单成功")
query_order("")
工程价值:全局统一异常格式,上层可统一捕获、统一返回前端错误信息、统一日志打印。
五、异常链 raise ... from(面试重难点)
异常链用于:捕获底层异常后,抛出上层新异常,保留原始异常堆栈信息,追溯报错根源。
语法:raise 新异常 from 原异常对象
场景:底层工具报错 → 上层封装业务异常,不丢失原始报错信息。
5.1 异常链标准实战
class DataParseError(Exception):
"""数据解析业务异常"""
pass
def parse_data(data):
try:
# 底层原生报错
num = int(data)
except ValueError as e:
# 抛出上层业务异常,并关联原始异常
raise DataParseError("数据转换失败,非有效数字") from e
# 调用测试
try:
parse_data("abc123")
except DataParseError as err:
print(f"捕获业务异常:{err}")
print(f"原始根因异常:{err.__cause__}")
5.2 异常链核心特性
1、通过 from e 绑定原始异常,自动形成异常调用链;
2、通过 异常对象.__cause__ 可获取底层根因;
3、报错堆栈同时展示:上层业务异常 + 底层原生异常,方便定位问题链路;
4、常用于:框架封装、工具类二次封装、业务层异常统一包装。
六、工程高频坑点汇总
1、空except吞异常:裸写 except: 不捕获具体类型,会隐藏所有报错,线上难以排查;
2、else代码不兜底:else属于正常分支,内部报错不会被上层except捕获;
3、finally覆盖return:若finally中写return,会强制覆盖try/except的return结果,导致逻辑异常;
4、滥用通用异常:直接捕获 Exception 所有异常,会掩盖意料外 bug;
5、抛弃原始异常:二次抛异常不使用 from,丢失底层报错堆栈,无法定位根因。
七、高频面试真题汇总
题目1:try-except-else-finally 执行顺序?else和finally区别?
答案:try优先执行,出错走except,无错走else,finally无论如何必执行。else仅正常执行分支,不捕获异常;finally用于资源释放、收尾,优先级最高。
题目2:raise 和 自定义异常的作用?
答案:raise用于主动抛出异常,拦截非法业务逻辑;自定义异常用于区分业务错误类型,统一项目异常规范,方便全局统一处理、日志统计、前端响应。
题目3:异常链 raise ... from 的原理和用途?
答案:用于封装上层新异常并绑定底层原始异常,保留完整报错链路,可通过 __cause__ 获取根因,解决二次封装异常丢失原始堆栈的问题,便于问题溯源。
题目4:finally 中写 return 会发生什么?
答案:会覆盖 try、except 中的 return 返回值,优先返回 finally 中的结果,属于工程禁忌写法,严禁在finally中写return。
八、全文终极背诵总结
1、四段式结构:try尝试执行、except捕获异常、else正常执行、finally最终收尾,各司其职,是工程标准写法。
2、raise主动抛异常:用于参数校验、业务拦截,单独raise可复刻原始异常,保留堆栈。
3、自定义异常:继承Exception,实现业务异常细分,统一项目错误规范,适配复杂业务场景。
4、异常链from:关联新旧异常,保留底层报错根因,解决封装后丢失原始堆栈的问题,提升可排查性。
5、工程原则:精准捕获异常、不吞报错、不滥用通用捕获、关键场景使用异常链溯源、资源释放放finally。