python-exceptions详细说明

python-exceptions详细说明

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("程序本次输入流程结束")
避坑:不要裸except: 不写异常类型会捕获所有Exception,无法区分错误,不利于排错。必须指定具体异常或Exception兜底。

四、常见内置原生异常场景与捕获示例

异常类触发场景简易捕获示例
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.py 统一异常定义 各service业务层主动raise自定义异常 入口main/cli统一捕获兜底 打印友好提示+记录日志

分层管理规范

  1. 公共层common/exceptions:集中定义全部自定义异常,全局统一;
  2. 业务service层:参数/配置校验时raise对应自定义异常,不打印日志;
  3. 最外层入口(main、cli、接口路由)统一try-except捕获;
  4. 区分自定义业务异常(友好中文提示)和系统原生异常(日志打印完整堆栈);
  5. 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)

Python Exceptions Full Beginner Guide: Understand, Use & Standardized Management

Basic syntax, try-except-else-finally, custom business exceptions, industrial project layer management

1 What is Exception

Exception is runtime error when syntax is correct but data/environment invalid. Unhandled exceptions crash program directly, break resource release. Catching exceptions keeps program alive with readable prompt.

Exception Hierarchy

BaseException → SystemExit / KeyboardInterrupt / Exception. All business errors inherit Exception, do not catch BaseException.

Core Syntax

try:
    risky_code()
except ValueError as e:
    handle_value_err()
except Exception as e:
    fallback()
else:
    run_when_no_error()
finally:
    always_run_release()

Built-in Exceptions List

ValueError / TypeError / IndexError / KeyError / FileNotFoundError / ZeroDivisionError / OSError

raise Statement

raise ValueError("Invalid input age")
# re-throw original error
try:1/0
except ZeroDivisionError:raise

Custom Exceptions

class BaseBizErr(Exception):
    def __init__(msg, code=400):
        super(msg)
        self.code = code
class EnvError(BaseBizErr):pass

Project Layer Management

1. Centralized exceptions.py in common folder

2. Service layer raise custom error

3. Entry main.py global catch & log

Best Rules

✅ Catch specific error, custom exception for business, log all errors, release resource in finally

❌ Bare except, catch BaseException, silent ignore errors

Full Template common/exceptions.py

class BaseAppErr(Exception):
    def __init__(self, msg, code=400):
        self.msg=msg
        self.code=code
class EnvMissingError(BaseAppErr):pass
class AICallError(BaseAppErr):pass