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 语句都会被跳过。

⚠️ 重要提醒: 千万不要用 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
💡 我的小技巧: 在单元测试里,assert 特别好用。我写测试用例时,几乎每行都离不开 assert。但记住——只在开发和测试阶段用,别让它跑到生产环境去。

2.4 避坑指南

我曾经踩过一个坑:用 assert 检查用户输入的用户名是否为空。结果上线后,用户提交空用户名,系统没报错,直接通过了。为什么?因为运维用 -O 参数启动了服务,所有 assert 都被跳过了。

所以记住:

  • ✅ assert 适合:调试、测试、内部逻辑校验
  • ❌ assert 不适合:用户输入验证、业务规则检查、安全校验

说白了,assert 是你写代码时的“小助手”,不是程序的“保安”。

2.5 知识体系总览

下面这张图,帮你把 assert 的核心知识点串起来:

assert 语句 语法 assert 条件, "信息" 工作原理 __debug__ 控制开关 适用场景 调试/测试/内部校验 条件为 True → 继续执行 条件为 False → 抛出 AssertionError python 正常执行 → assert 生效 python -O 执行 → assert 跳过 ✅ 内部逻辑检查 ✅ 前置/后置条件检查 ❌ 用户输入/业务校验 核心:调试时抓 Bug,别让它干保安的活

看到这张图,你应该能明白 assert 的定位了。它就像你写代码时的一个“哨兵”,帮你盯着那些“不应该发生”的事。一旦发生了,立刻喊停。

记住一句话:assert 是写给开发者的,不是写给用户的。