2. assert语句:断言语法、工作原理与适用场景
调试代码时,我最怕一种情况——程序跑了大半天,最后在某个角落崩了,你翻遍日志也找不到问题出在哪。后来我养成一个习惯:在关键位置插上 assert。说白了,assert 就是帮你提前“抓现行”的。
2.1 assert 语法
Python 的 assert 语法非常简单,就一行:
assert 条件表达式, "可选的错误信息"
如果条件为 True,啥事没有,程序继续往下走。
如果条件为 False,立马抛出 AssertionError,并打印你写的错误信息。
举个例子:
def divide(a, b):
assert b != 0, "除数不能为0"
return a / b
print(divide(10, 2)) # 正常输出 5.0
print(divide(10, 0)) # 抛出 AssertionError: 除数不能为0
嗯,这里要注意:assert 后面的错误信息不是必须的。但我个人习惯一定会写上,因为调试时看到“除数不能为0”比看到“AssertionError”要舒服得多。
2.2 assert 的工作原理
你可能会问:assert 到底是怎么工作的?其实很简单。
Python 解释器在执行 assert 时,等价于下面这段代码:
if __debug__:
if not 条件表达式:
raise AssertionError(错误信息)
看到 __debug__ 这个变量了吗?它默认是 True。但如果你用 python -O 运行脚本,__debug__ 就变成 False,所有 assert 语句都会被跳过。
-O 参数运行,assert 会被直接忽略。我在项目中就见过有人用 assert 检查用户输入,结果上线后所有校验都失效了……
举个例子说明跳过的情况:
# test.py
def main():
x = 1
assert x == 2, "x 应该等于2"
print("程序继续")
if __name__ == "__main__":
main()
正常运行时:抛出 AssertionError。
用 python -O test.py 运行时:直接打印“程序继续”,assert 被跳过了。
2.3 assert 的适用场景
assert 到底该用在哪儿?我总结了三类场景,你想想看是不是这个理:
| 场景 | 说明 | 示例 |
|---|---|---|
| 内部逻辑检查 | 确保程序状态符合预期 | 函数返回值不应为 None |
| 前置条件检查 | 函数调用前参数必须满足的条件 | 列表不能为空 |
| 后置条件检查 | 函数执行后结果必须满足的条件 | 排序后长度不变 |
场景一:内部逻辑检查
这是我最常用的场景。比如写一个二分查找:
def binary_search(arr, target):
assert len(arr) > 0, "数组不能为空"
left, right = 0, len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
result = binary_search([1, 3, 5, 7, 9], 5)
assert result == 2, f"期望索引2,实际得到{result}"
我曾经在写排序算法时,忘了检查数组是否有序,结果 debug 了一下午。后来我习惯在关键函数前后都加上 assert,问题一下子就暴露了。
场景二:前置条件检查
函数开始执行前,先确认参数是合法的:
def calculate_average(scores):
assert len(scores) > 0, "成绩列表不能为空"
assert all(0 <= s <= 100 for s in scores), "成绩必须在0-100之间"
return sum(scores) / len(scores)
场景三:后置条件检查
函数执行完后,确认结果没毛病:
def sort_list(items):
result = sorted(items)
assert len(result) == len(items), "排序后长度不能变"
assert all(result[i] <= result[i+1] for i in range(len(result)-1)), "排序结果必须升序"
return result
2.4 避坑指南
我曾经踩过一个坑:用 assert 检查用户输入的用户名是否为空。结果上线后,用户提交空用户名,系统没报错,直接通过了。为什么?因为运维用 -O 参数启动了服务,所有 assert 都被跳过了。
所以记住:
- ✅ assert 适合:调试、测试、内部逻辑校验
- ❌ assert 不适合:用户输入验证、业务规则检查、安全校验
说白了,assert 是你写代码时的“小助手”,不是程序的“保安”。
2.5 知识体系总览
下面这张图,帮你把 assert 的核心知识点串起来:
看到这张图,你应该能明白 assert 的定位了。它就像你写代码时的一个“哨兵”,帮你盯着那些“不应该发生”的事。一旦发生了,立刻喊停。
记住一句话:assert 是写给开发者的,不是写给用户的。