Python 异常处理

本文面向已有前端开发基础、正在学习 Python 的开发者。

用前端开发中熟悉的 try...catchthrow、错误对象和接口错误处理,理解 Python 的异常处理。

异常处理的目的不是把错误藏起来,而是在程序运行到"不符合预期"的地方时,明确决定下一步怎么做。

如果你有 JavaScript / TypeScript 经验,可以先用这张表建立直觉:

Python 知识点 前端脑内映射 先记住
try try 包住可能出错的代码
except catch 出错后进入这里处理
except ValueError catch 里判断错误类型 只处理某一种错误
as e catch (error) 拿到错误对象
else JS 没有完全对应语法 try 没出错时执行
finally finally 不管成功失败都执行
raise throw 主动抛出错误
Exception Error 常见异常的通用父类
自定义异常 class XxxError extends Error 让错误类型更有业务含义
lua 复制代码
Python 异常处理

  发现错误
     |
     +-- Python 自动抛出异常
     +-- 自己用 raise 主动抛出异常
     |
  捕获错误
     |
     +-- try / except
     +-- except XxxError as e
     |
  处理结果
     |
     +-- else 成功后执行
     +-- finally 最后一定执行
     |
  工程表达
     |
     +-- 自定义异常
     +-- raise ... from e
     +-- traceback / 日志

先记一句话:

复制代码
异常 = 程序运行过程中,某一步无法继续正常执行

一、先建立总地图

学习阶段先熟悉这些常见异常:

异常类型 前端脑内映射 常见原因
NameError 变量没定义 使用了不存在的变量名
TypeError 类型错误 类型不支持当前操作
ValueError 值不合法 值的格式不对
KeyError 读对象上不存在的 key 字典里没有这个 key
IndexError 数组下标越界 列表下标越界
AttributeError null / 错误对象取属性 对象没有这个属性或方法
ZeroDivisionError 除法边界错误 除数是 0

从前端角度看,JS 里很多运行时问题最后都表现成 TypeError 或普通 Error。Python 会把错误分得更细,比如字典 key 不存在是 KeyError,列表下标越界是 IndexError

简单演示。下面每一行都是独立示例,不是让它们一次性全部运行:

python 复制代码
name # NameError,变量不存在

10 + '1' # TypeError,类型不支持当前操作

int('abc') # ValueError,值的格式不对

user = {}
user['name'] # KeyError,字典里没有这个 key

items = []
items[0] # IndexError,列表下标越界

value = None
value.strip() # AttributeError,对象没有这个方法

10 / 0 # ZeroDivisionError,除数是 0

二、try except

1. 前端脑内映射

Python 的 try...except 对应 JS 的 try...catch

把可能出错的代码放进 try,把出错后的处理放进 except

python 复制代码
try:
    age = int('abc')
except ValueError:
    print('年龄必须是数字')

对应 JS 里的写法大概是:

js 复制代码
try {
  const age = Number.parseInt('abc', 10);

  if (Number.isNaN(age)) {
    throw new Error('年龄必须是数字');
  }
} catch (error) {
  console.log(error.message);
}

差异在于:Python 的 int('abc') 会直接抛出 ValueError,而 JS 的 Number.parseInt('abc', 10) 会得到 NaN,你通常要自己判断。

2. 执行顺序

执行顺序:

rust 复制代码
try 里的代码正常
  -> 继续往下执行

try 里的某一行出错
  -> 后面的 try 代码不再执行
  -> 跳到匹配的 except

多个异常可以分别处理:

python 复制代码
data = {'count': 'abc'}

try:
    count = int(data['count'])
except KeyError:
    print('缺少 count 字段')
except ValueError:
    print('count 必须是数字')

这点和 JS 最大的区别是:Python 可以按异常类型分多个 except;JS 通常是在一个 catch 里自己判断错误类型。

3. 多个异常合并处理

多个异常处理逻辑一样,可以写成元组:

python 复制代码
try:
    count = int(data['count'])
except (KeyError, ValueError):
    print('count 参数不正确')

不要随手写空 except

python 复制代码
try:
    count = int(data['count'])
except:
    print('出错了')

这种写法会捕获太多错误,容易把真正的问题藏起来。

4. Exception 兜底放最后

如果需要兜底,写 Exception,并且放在最后:

python 复制代码
try:
    count = int(data['count'])
except KeyError:
    print('缺少 count')
except ValueError:
    print('count 必须是数字')
except Exception as e:
    print(f'未知异常:{e}')

多个 except 会从上往下匹配。越具体的异常越应该写在前面,越宽泛的异常越应该写在后面。

三、获取异常信息

1. 前端脑内映射

JS 里常见写法是 catch (error),Python 里对应的是 except XxxError as e

js 复制代码
try {
  throw new Error('年龄必须是数字');
} catch (error) {
  console.log(error.message);
  console.log(error.name);
}

2. Python 写法

as e 可以拿到异常对象。

python 复制代码
try:
    age = int('abc')
except ValueError as e:
    print(e) # 错误提示
    print(type(e).__name__) # 异常类型名称
    print(e.args) # 异常参数

常用信息:

写法 作用
str(e) 错误提示文本
type(e).__name__ 异常类型名称
e.args 异常参数

如果想看完整调用栈,用标准库 traceback

python 复制代码
import traceback

try:
    age = int('abc')
except ValueError:
    print(traceback.format_exc())

traceback 可以理解成 Python 里的 stack trace,能看到错误发生在哪个文件、哪一行、经过了哪些函数调用。

学习阶段可以用 print。真实项目里通常用日志记录,不要只留一句"出错了"。

四、else、finally 和 raise

1. try / except / else / finally

完整结构是:

rust 复制代码
try
  -> 尝试执行

except
  -> 出错时执行

else
  -> 没有出错时执行

finally
  -> 无论是否出错,最后都执行

前端熟悉的 try...catch...finally 在 Python 里多了一个常用分支:else

vbnet 复制代码
JS: try / catch / finally
Python: try / except / else / finally

例子:

python 复制代码
try:
    age = int('18')
except ValueError:
    print('年龄必须是数字')
else:
    print(f'转换成功:{age}')
finally:
    print('处理结束')

finally 常用于释放资源,比如关闭文件、关闭数据库连接、释放锁。

2. raise 主动抛出异常

有些错误不是 Python 自动发现的,而是你根据规则判断出来的,这时用 raise 主动抛出异常。

python 复制代码
def parse_age(value):
    age = int(value)

    if age < 0:
        raise ValueError('年龄不能小于 0')

    return age

对应 JS:

js 复制代码
function parseAge(value) {
  const age = Number(value);

  if (age < 0) {
    throw new Error('年龄不能小于 0');
  }

  return age;
}

调用方再决定怎么处理:

python 复制代码
try:
    age = parse_age('-1')
except ValueError as e:
    print(f'参数错误:{e}')

函数内部负责发现问题并 raise,函数外部再用 try except 决定怎么处理。

五、自定义异常和异常链

1. 自定义异常

内置异常能表达通用问题,比如 ValueError 表示值不合法。

如果你想表达更具体的业务含义,可以自定义异常。

前端里如果要区分不同错误,可能会写:

ts 复制代码
class UserNotFoundError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'UserNotFoundError';
  }
}

Python 里通常这样写:

python 复制代码
class UserError(Exception):
    """用户相关异常的基类。"""


class UserNotFoundError(UserError):
    """用户不存在。"""

使用:

python 复制代码
def find_user(user_id):
    users = {1: 'Tom', 2: 'Jerry'}

    if user_id not in users:
        raise UserNotFoundError(f'用户不存在:{user_id}')

    return users[user_id]

捕获:

python 复制代码
try:
    name = find_user(3)
except UserNotFoundError as e:
    print(e)

自定义异常的好处是:异常类型本身就能表达含义。

rust 复制代码
ValueError
  -> 只能知道"值不对"

UserNotFoundError
  -> 一眼知道"用户不存在"

如果一组异常属于同一类,可以定义共同父类:

python 复制代码
class AppError(Exception):
    """项目异常基类。"""


class ConfigError(AppError):
    """配置错误。"""


class AppPermissionError(AppError):
    """权限错误。"""

这样可以统一捕获:

python 复制代码
try:
    raise ConfigError('缺少数据库配置')
except AppError as e:
    print(f'应用异常:{e}')

2. raise from 保留异常链

有时底层错误比较细,直接暴露给上层不够清楚。可以用 raise ... from e 转换异常,同时保留原始原因。

python 复制代码
class AgeError(Exception):
    """年龄参数错误。"""


def parse_age(value):
    try:
        return int(value)
    except ValueError as e:
        raise AgeError('年龄必须是数字') from e

含义:

arduino 复制代码
表层原因:AgeError,年龄必须是数字
底层原因:ValueError,int 转换失败

这样既能给上层一个清楚的异常类型,又不会丢掉原始错误。

六、实践原则

异常处理最容易出问题的地方,不是语法,而是捕获范围太大、处理太随意。

从前端经验迁移过来,可以这样理解:

arduino 复制代码
不要到处 catch 后什么都不做
不要把所有错误都变成一个"系统异常"
不要给用户暴露完整 stack trace
不要把开发排查需要的信息丢掉
  • 不要随手写空 except:
  • 不要把所有代码都包进一个巨大的 try
  • 能捕获具体异常,就不要一开始捕获 Exception
  • Exception 兜底要放在最后
  • 捕获异常后要处理、记录,或者继续抛出,不要悄悄吞掉
  • 自定义异常通常继承 Exception
  • 函数内部可以只负责 raise,外层再统一 try except
  • 给用户看的提示要简洁,给开发看的错误信息要完整
相关推荐
计算机魔术师9 分钟前
NVIDIA 季度营收指引达 1080 亿美元,首次突破单季千亿大关
前端
向生21 分钟前
Ubuntu Caddy 保姆级完整教程
后端
数据狐(Datafox)23 分钟前
京东商品列表API技术解析与落地应用(含标准 JSON 示例)
java·大数据·前端·人工智能·python·数据分析·json
风萧何25 分钟前
架构艺术,是权衡的艺术。
前端·javascript·架构
YIAN26 分钟前
ESLint 新版 Flat Config 实战:团队代码风格统一,从这一份配置开始
前端·团队管理
计算机魔术师27 分钟前
英伟达单季营收逼近千亿美元,黄仁勋放话:真实需求远超想象
前端
tntxia27 分钟前
Docker权限的问题
后端
明月_清风28 分钟前
看完这段关于"全插件化架构"的技术分析后,我整理了一份笔记
前端·后端
量化吞吐机29 分钟前
2026年下半年手工交易规则走向量化表达,产品该先接住哪一步
人工智能·python
YIAN33 分钟前
Next.js App Router 全栈实战:从 0 到 1 写一个 Todo 应用,前端后端一个项目搞定
前端·全栈·next.js