Python 中的 assert 语句:基本语法与 AssertionError
调试代码这件事,我干了十几年。说实话,最让我头疼的不是复杂的逻辑错误,而是那些「明明感觉没问题,一跑就崩」的情况。后来我养成了一个习惯——在关键位置插上 assert 语句。这玩意儿看着简单,用好了能省下大把抓头发的时间。
今天咱们就聊聊 Python 里的 assert。别小看它,很多老手都用不对。
assert 的基本语法
先看最基础的样子:
assert 条件表达式
如果条件为 True,啥事没有,程序继续往下走。
如果条件为 False,立刻抛出 AssertionError,程序停住。
举个例子:
def divide(a, b):
assert b != 0
return a / b
print(divide(10, 2)) # 输出 5.0
print(divide(10, 0)) # 抛出 AssertionError
运行到第二行 divide(10, 0) 时,程序直接炸了。嗯,这就是 assert 的脾气——它不跟你商量。
带错误信息的 assert
光抛个异常还不够友好。你想想看,线上日志里只看到一行 AssertionError,你根本不知道是哪个条件没满足。所以我一般会加一段描述信息:
assert 条件表达式, "错误描述信息"
比如:
def withdraw(balance, amount):
assert amount > 0, "取款金额必须大于0"
assert amount <= balance, f"余额不足,当前余额: {balance}"
return balance - amount
这样当断言失败时,你能立刻看到具体原因。我在项目中遇到过好几次,就是因为没加描述信息,排查问题花了整整一下午。从那以后,我所有的 assert 都带上了描述。
AssertionError 的本质
说白了,AssertionError 就是 Python 内置的一个异常类。它继承自 Exception。你可以像捕获其他异常一样捕获它:
try:
assert 1 == 2, "1不等于2"
except AssertionError as e:
print(f"断言失败: {e}")
输出:
断言失败: 1不等于2
但我要提醒你——不要用 try-except 来捕获 AssertionError。为什么?因为 assert 的本意是「这里不应该出现这种情况」。如果你捕获了它,等于在说「出错了也没关系」,那还不如直接用 if 判断。
assert 的开关控制
这一点很多人不知道——assert 是可以全局关闭的。
当你用 -O 或 -OO 参数运行 Python 时,所有 assert 语句都会被忽略。比如:
# 正常运行
python my_script.py
# 关闭 assert
python -O my_script.py
为什么会这样?因为 Python 把 assert 当成调试工具。生产环境里,为了性能,你可以关掉它。但这也意味着——不要把 assert 用在必须执行的逻辑上。
我曾经见过一个同事,在支付接口里用 assert 检查金额:
assert amount > 0, "金额非法"
结果上线时加了 -O 参数,这个检查直接失效了。嗯,后果你可以想象。所以记住:
什么时候该用 assert?
我个人的经验是,以下场景特别适合:
- 函数前置条件检查:比如参数不能为 None、列表不能为空
- 函数后置条件检查:比如返回值必须在某个范围内
- 不可能发生的情况:比如 switch-case 的 default 分支
- 单元测试中的断言:虽然 unittest 有自己的 assert 方法,但原生 assert 也够用
举个例子:
def process_user_data(data):
assert data is not None, "数据不能为空"
assert "name" in data, "缺少 name 字段"
result = do_something(data)
assert result["status"] in (0, 1), f"未知状态: {result['status']}"
return result
你看,我在函数开头和结尾都加了 assert。开头保证输入没问题,结尾保证输出符合预期。这叫「契约式编程」,说白了就是让代码自己给自己做检查。
避坑指南
我曾经踩过一个坑,分享给你:
不要用 assert 检查用户输入。 用户输入是不可信的,而且你不能因为用户输错了就抛 AssertionError。正确的做法是用 if 判断,然后返回友好的错误信息。
还有一点:assert 后面的表达式不要有副作用。比如:
# 错误示范
assert pop_item() is not None # pop_item 会修改列表
# 正确做法
item = pop_item()
assert item is not None
因为 assert 可能被关闭,如果表达式有副作用,关闭后行为就不一致了。
知识体系总览
下面这张图,帮你把本章的核心内容串起来:
这张图把 assert 的五个核心维度串在了一起。你写代码时,可以对照着看看自己用对了没有。
总结一下
assert 是个好东西,但用不好就是坑。记住三条:
- 只用于调试和测试,别用在正式逻辑里
- 带上描述信息,方便排查问题
- 表达式不要有副作用,否则关了 assert 行为就变了
嗯,今天就聊到这儿。assert 看着简单,但背后这些门道,够你琢磨一阵子了。