divmod 比分别调用 // 和 % 更快,因其底层 c 实现复用一次除法运算;但仅在同时需要商和余数时成立,单独使用时应直接写 // 或 % 以保证清晰性和避免额外开销。

divmod 为什么比分别调用 // 和 % 更快
在 Python 中,divmod(a, b) 一次调用就返回商和余数的元组,底层 C 实现复用了同一轮除法运算逻辑;而 a // b 和 a % b 分别调用会各自执行一遍除法——尤其对大整数或自定义 __divmod__ 类型时,开销更明显。CPython 源码里 divmod 确实只做一次 long_divrem 或浮点除法。
不过这个优势仅在「你需要两者」时成立。如果只用商或只用余数,单独写 // 或 % 更清晰,且 JIT(如 PyPy)可能已做部分优化,实际差异未必可观。
divmod 在整数、浮点、负数下的行为细节
divmod 的结果必须满足恒等式:divmod(a, b) == (a // b, a % b),但要注意 Python 的地板除(floor division)规则:商总是向下取整,余数符号与除数一致。
- 正数:
divmod(17, 5)→(3, 2) - 负被除数:
divmod(-17, 5)→(-4, 3)(因为-4 * 5 + 3 == -17) - 负除数:
divmod(17, -5)→(-4, -3)(余数为负,与除数同号) - 浮点数:
divmod(7.5, 2.0)→(3.0, 1.5),但注意浮点精度误差可能让余数不精确为 0(如divmod(0.3, 0.1)返回(2.0, 0.09999999999999998))
什么时候不该用 divmod
不是所有场景都适合强行套用 divmod。以下情况建议拆开写:
- 只用其中一个结果,比如只关心余数做奇偶判断:
if n % 2 == 0:比if divmod(n, 2)[1] == 0:更直白且无额外元组开销 - 除数是变量且可能为 0:虽然
divmod(a, 0)和a // 0/a % 0都抛ZeroDivisionError,但错误栈更难定位具体是哪一步触发的 - 配合类型提示或静态检查(如 mypy):
divmod返回tuple[int, int]或tuple[float, float],但若后续只解包一个,mypy 可能报Need type annotation for ...或误判类型流 - 自定义类重载了
//和%但没实现__divmod__:此时divmod(obj, b)会退回到分别调用,反而多一层判断,不如直接写两个操作符
真实性能对比示例与建议写法
用 timeit 测整数场景(CPython 3.12):
安全 Python 代码沙箱:在隔离的沙箱中执行任意 Python 代码,预装 requests、NumPy、pandas 库。返回 stdout、stderr。
python -m timeit -s "a, b = 1234567, 89" "a // b; a % b" python -m timeit -s "a, b = 1234567, 89" "divmod(a, b)"
结果通常显示 divmod 快 10%–15%。但注意:这种差距只在高频循环中才有意义,比如写解析器处理大量字节偏移、或密码学中模幂内层循环。
推荐写法是「解包即用」,避免临时变量:
quotient, remainder = divmod(total_bytes, chunk_size) # 而不是 result = divmod(total_bytes, chunk_size) quotient = result[0] remainder = result[1]
另外,若涉及多次同类运算(如批量计算坐标格点),可考虑用 NumPy 的 np.divmod,它对数组整体向量化,性能提升远超单个 divmod。
真正容易被忽略的是浮点 divmod 的精度陷阱——它不会帮你绕过 IEEE 754 问题,该用 decimal 或整数缩放时,别依赖 divmod「看起来更统一」就硬上。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










