直接用 timeit.timeit() 就够了,除非怀疑结果受系统抖动影响;timeit.repeat() 适合需排除异常值的严谨场景,但基础对比无需过度谨慎。

timeit.timeit() 和 timeit.repeat() 选哪个?
直接用 timeit.timeit() 就够了,除非你怀疑结果受系统抖动影响。它默认执行 100 万次并返回总耗时(秒),适合快速比对两种写法;而 timeit.repeat() 会多次调用 timeit.timeit() 并返回数组,方便手动排除异常值——但多数基础语法对比不需要这么谨慎。
常见错误是只跑一次就下结论。比如测试 a = [1,2,3] 和 a = list((1,2,3)),单次运行可能因 GC 或 CPU 调度偏差差出 10%。建议至少用 number=1000000,或用 repeat(repeat=3, number=1000000) 取最小值。
setup 参数为什么不能省略?
因为 timeit 在独立作用域中执行代码,外部变量、导入、函数定义全不可见。比如想测 json.loads(s),不通过 setup 导入 json 和定义 s,就会报 NameError: name 'json' is not defined。
实操建议:
- 把所有依赖写进字符串:
setup="import json; s='[1,2,3]'" - 避免在
stmt里做初始化(如stmt="s=[1,2,3]; s.append(4)"),否则测的是赋值 + append,不是纯 append 效率 - 用
lambda或预定义函数更清晰:setup="def f(): return [1,2,3]",stmt="f().append(4)"
字符串 stmt 和 callable stmt 哪种更准?
callable 更准,尤其当语句含变量或需复用对象时。字符串形式会触发额外的解析开销,且无法复用 setup 中创建的对象引用。
例如测 list.append() vs list += [x]:
import timeit
l = [1, 2, 3]
timeit.timeit(lambda: l.append(4), number=1000000) # ✅ 复用同一列表
timeit.timeit("l.append(4)", setup="l=[1,2,3]", number=1000000) # ❌ 每次都新建 l
后者每次执行前都会重新运行 setup,导致测的是“新建列表 + append”,而非仅 append 动作本身。
为什么 sys.getsizeof() 不能替代 timeit 测内存操作效率?
sys.getsizeof() 返回对象当前内存占用,和执行时间无关。比如 [1]*1000 和 list(range(1000)) 内存相近,但后者涉及整数对象创建、迭代器消耗,实际耗时高 3–5 倍。
真正要对比内存友好型写法(如生成器 vs 列表推导),必须用 timeit 实测执行时间,并注意:
- 生成器表达式
(x for x in range(1000))创建快、启动慢;列表推导[x for x in range(1000)]创建慢、后续访问快 - 若测试含
next()或sum()等消费动作,必须包含在stmt中,否则只测了构造开销 - 小数据量(如
range(10))下差异常被 Python 解释器优化掩盖,建议从range(1000)起步
最易被忽略的是作用域隔离带来的副作用——哪怕只差一个点号(list.append vs l.append),Python 查找路径不同,耗时能差 15% 以上。别信直觉,测。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











