Python异常(Exception)零基础完整教程:理解、使用、标准化管理
零基础大白话讲解,覆盖原生异常、try-except-finally-else、自定义业务异常、分层统一异常管理、工程落地规范全内容
目录
一、什么是Exception异常?为什么要处理异常
1.1 零基础通俗解释
异常(Exception):程序运行过程中发生的非预期错误。代码语法错误是编译报错,异常是语法没问题,但运行时环境/数据不满足要求导致崩溃。
举例子:
- 读取不存在文件 → FileNotFoundError
- 字符串转数字失败 → ValueError
- 网络接口超时 → 第三方库异常
- 数组下标超出范围 → IndexError
1.2 不处理异常的致命问题
不捕获异常(缺点)
1. 程序直接闪退,控制台黑窗口瞬间关闭,用户看不到报错;
2. 无友好中文提示,新手看不懂英文报错栈;
3. 中间资源(文件、数据库连接)不会自动关闭,造成资源泄漏;
4. 无法做重试、降级、日志记录等容错逻辑。
捕获处理异常(优势)
1. 程序不会直接崩溃,给出清晰可读的中文提示;
2. 自动释放文件/网络/数据库资源;
3. 可实现自动重试、兜底默认值、日志记录错误;
4. 区分不同错误类型,针对性处理。
二、Python异常底层原理与继承层级
2.1 异常继承树(核心结构)
BaseException
├── SystemExit # sys.exit()程序退出(不建议捕获)
├── KeyboardInterrupt # Ctrl+C手动终止程序
└── Exception # 所有业务运行异常父类(日常捕获起点)
├── ArithmeticError(数值)
│ └── ZeroDivisionError 除零错误
├── LookupError(索引/键)
│ ├── IndexError 列表下标越界
│ └── KeyError 字典键不存在
├── OSError(文件/系统)
│ └── FileNotFoundError 文件不存在
├── ValueError 值类型错误
├── TypeError 类型不匹配
└── 自定义业务异常(继承Exception)
Exception 及其子类,不要捕获BaseException,会拦截Ctrl+C退出信号,导致程序无法正常关闭。
三、基础语法:try / except / else / finally 完整用法
3.1 四段语法完整结构
try:
# 【核心监控区】可能报错的代码放这里
code_that_may_error()
except 指定异常类型 as e:
# 【异常分支】捕获到对应错误执行
handle_error(e)
except Exception as e:
# 【兜底分支】所有未匹配的普通异常
handle_all_error(e)
else:
# 【无异常分支】try代码全部成功、无报错才执行
success_logic()
finally:
# 【必执行分支】无论是否报错、是否return、是否break都会运行
release_resource()
各区块执行规则
- try:必须存在;
- except:至少一个,可多个区分不同错误;
- else:可选,仅无异常触发;
- finally:可选,无论如何都会执行,用于关闭文件、连接。
完整可运行示例
try:
num = int(input("输入数字:"))
res = 10 / num
except ValueError:
print("输入不是数字!")
except ZeroDivisionError:
print("不能除以0!")
except Exception as e:
print("未知错误", e)
else:
print("计算结果:", res)
finally:
print("程序本次输入流程结束")
四、常见内置原生异常场景与捕获示例
| 异常类 | 触发场景 | 简易捕获示例 |
|---|---|---|
| ValueError | 值格式错误,字符串转数字失败 | except ValueError as err: |
| TypeError | 类型不匹配,数字+字符串运算 | except TypeError as err: |
| IndexError | 列表下标超过长度 | except IndexError as err: |
| KeyError | 字典无对应key | except KeyError as err: |
| FileNotFoundError | 文件路径不存在 | except FileNotFoundError as err: |
| ZeroDivisionError | 除法分母为0 | except ZeroDivisionError as err: |
| OSError | 文件权限、系统操作失败 | except OSError as err: |
五、主动抛出异常 raise 语法
5.1 作用说明
捕获是处理运行自动产生的错误,raise是手动主动抛出异常,用于参数校验、业务规则拦截。
5.2 基础语法
# 基础抛出
raise ValueError("年龄必须大于0")
# 捕获后重新抛出原有异常
try:
1/0
except ZeroDivisionError as e:
logger.error(e)
raise # 不带参数,原样抛出原异常
5.3 业务校验示例
def check_age(age):
if not isinstance(age, int):
raise TypeError("年龄必须是整数")
if age < 0 or age > 150:
raise ValueError("年龄范围0~150")
check_age(-5)
六、自定义业务异常(工业项目必备)
6.1 为什么要自定义异常
原生异常只有通用类型,无法区分业务场景:比如同样ValueError,分不清是用户输入错误还是AI密钥缺失。自定义异常可以精准区分业务模块,给出专属提示,统一错误码。
6.2 标准自定义异常写法(继承Exception)
# 所有业务异常父类
class BaseBusinessError(Exception):
"""项目所有业务异常基类"""
def __init__(self, msg: str, code: int = 400):
self.msg = msg
self.code = code
super().__init__(self.msg)
# 细分场景异常
class EnvMissingError(BaseBusinessError):
""".env环境变量缺失异常"""
def __init__(msg="未读取到环境密钥", code=401):
super(msg, code)
class AIRequestError(BaseBusinessError):
"""AI接口调用失败异常"""
class FileOperateError(BaseBusinessError):
"""文件读写异常"""
6.3 使用方式
from common.exceptions import EnvMissingError
api_key = None
if not api_key:
raise EnvMissingError("AI_API_KEY未配置,请检查.env文件")
七、工程化统一异常管理方案(分层封装)
分层管理规范
- 公共层common/exceptions:集中定义全部自定义异常,全局统一;
- 业务service层:参数/配置校验时raise对应自定义异常,不打印日志;
- 最外层入口(main、cli、接口路由)统一try-except捕获;
- 区分自定义业务异常(友好中文提示)和系统原生异常(日志打印完整堆栈);
- finally统一释放资源:文件、网络连接、数据库会话。
全局入口统一捕获示例
from common.exceptions import BaseBusinessError
from common.logger import logger
def entry():
try:
business_logic()
# 优先捕获自定义业务异常
except BaseBusinessError as e:
print(f"业务提示:{e.msg}")
logger.warning(f"业务异常 code={e.code} msg={e.msg}")
# 兜底捕获系统未知异常
except Exception as e:
print("程序发生未知错误,请查看日志")
logger.critical("未知崩溃", exc_info=True)
finally:
# 释放资源
close_all_connect()
八、异常处理最佳实践 & 避坑红线
✅ 推荐规范
- 精准捕获具体异常,不使用裸except;
- 业务场景全部自定义异常,区分错误码与提示;
- 只在最外层入口统一捕获,业务中间层尽量向上抛出;
- 文件/连接操作使用finally或with自动释放;
- 异常必须记录日志,区分warning/error/critical等级;
- 不要吞掉异常不做任何处理(只except不打印日志)。
❌ 禁止踩坑操作
- 裸except: 捕获全部异常,无法定位问题;
- 捕获BaseException,拦截Ctrl+C退出;
- 捕获异常后无日志、无提示,静默失败;
- 多层重复捕获同一异常,堆栈混乱;
- 大量try包裹整段代码,粒度太粗,分不清哪行报错。
九、完整项目落地模板(common统一异常文件)
文件路径:common/exceptions.py
"""项目全局统一自定义异常模块"""
class BaseAppErr(Exception):
"""程序所有业务异常父类"""
def __init__(self, message: str, err_code: int = 400):
self.message = message
self.err_code = err_code
super().__init__(self.message)
# 环境配置类异常
class EnvMissingError(BaseAppErr):
def __init__(self, msg="缺失必要环境配置", code=401):
super(msg, code)
# AI接口类异常
class AICallError(BaseAppErr):
pass
# 文件操作异常
class FileIoError(BaseAppErr):
pass
# 参数校验异常
class ParamInvalidError(BaseAppErr):
pass
使用示例 config/settings.py
import os
from common.exceptions import EnvMissingError
API_KEY = os.getenv("AI_API_KEY")
if not API_KEY:
raise EnvMissingError("AI_API密钥未填写,请修改.env.dev文件")
入口main.py全局捕获
from common.exceptions import BaseAppErr
from common.logger import logger
from api.cli import run_cli
if __name__ == "__main__":
try:
run_cli()
except KeyboardInterrupt:
logger.info("用户手动终止程序")
except BaseAppErr as e:
print(f"\n【业务错误】{e.message}")
logger.warning(f"业务异常 code={e.err_code} {e.message}")
except Exception as err:
print("\n【未知系统错误】详细信息请查看日志")
logger.critical("程序崩溃", exc_info=True)
