Python divmod()函数:怎么同时获取商和余数

小鹿同学
小鹿同学
发布时间:2026-10-26 | 3人浏览过
原创

假设你正在编写一个视频播放器的时间显示模块,需要把服务端返回的 7385 秒转换为直观的“几小时几分钟几秒”;又或者你在实现分页查询功能,手头有 105 条数据、每页固定展示 20 条,需要准确计算出总共有多少整页以及最后一页还剩余几条。面对这类需求,代码必须同时得出整除后的“商”和切分剩下的“余数”。在 Python 中,常见做法是先后写两行代码:一行用 // 算商,另一行用 % 算余数。但 Python 实际上提供了一个内置函数 divmod(),只需一步调用就能将商与余数打包成元组同时返回。本文将系统讲解 divmod() 的基本用法、底层运作逻辑、常见实战场景以及负数和浮点数相关的边界细节。

一、快速上手:divmod() 的语法与基本返回值

divmod() 是 Python 的内置函数(built-in function),这意味着你在任何 Python 脚本或交互式终端中无需 import 任何模块,即可直接调用。它的语法非常直接:

divmod(a, b)

其中,参数 a 是被除数(numerator),参数 b 是除数(denominator)。函数的返回值是一个包含两个元素的不可变元组 (q, r),第一个元素 q 是整数商(quotient),第二个元素 r 是余数(remainder)。

通常情况下,我们配合 Python 的序列解包(tuple unpacking)语法来接收结果:

# 示例:计算 17 除以 5 的商和余数
quotient, remainder = divmod(17, 5)

print("商:", quotient)     # 输出: 商: 3
print("余数:", remainder)   # 输出: 余数: 2

在数学关系上,返回值始终满足除法恒等式:a == quotient * b + remainder。对于正整数运算而言,17 == 3 * 5 + 2 完全吻合。

Python divmod()函数:怎么同时获取商和余数
图 1:双运算符执行模式与 divmod() 内置机制对比

二、深入原理:divmod() 与双运算符的机制差异

很多开发者会产生疑问:既然已经有了 // 和 %,为什么语言设计者还要专门加入一个 divmod()?这主要关乎执行效率和语义完整性。

在底层实现中,Python 官方解释器(CPython)针对整数类型实现了专门的 C 函数 long_divmod。现代 CPU 的硬件除法指令(例如 x86 架构上的 IDIV 指令)在执行一次硬件除法时,会同时把商存放在一个寄存器(如 RAX),把余数存放在另一个寄存器(如 RDX)。

当你分别书写 a // b 和 a % b 时,Python 解释器需要执行两次操作:

  • 在虚拟机层面,分别生成 BINARY_FLOOR_DIVIDE 和 BINARY_MODULO 字节码;
  • 两次查找变量 a 和 b 并分别压入求值栈;
  • 分别调用两次底层的除法相关逻辑。

而直接使用 divmod(a, b) 时,解释器只需完成一次函数调用,底层直接调用对象类型的 tp_as_number->nb_divmod 槽函数,单次计算便同时拿到商和余数,并在 C 层面直接构造成 Python 元组返回。尤其是在处理超大整数(大数运算)时,大整数除法代价高昂,divmod() 避免了昂贵的二次除法开销。

Python divmod()函数:怎么同时获取商和余数
图 2:divmod() 调用生命周期与内部步骤

三、实战场景:divmod() 的经典使用案例

1. 时间秒数换算为时、分、秒

这是 divmod() 最具代表性的实战场景。要把总秒数换算成符合常规展示的 HH:MM:SS 格式,只需两次级联调用:首先对 3600 秒取商取余得到小时数和剩余秒数,再对 60 取商取余得到分钟数和最终秒数。

def format_seconds(total_seconds: int) -> str:
    # 步骤一:提取小时与剩余秒数
    hours, remainder = divmod(total_seconds, 3600)
    # 步骤二:从剩余秒数中提取分钟和最终秒数
    minutes, seconds = divmod(remainder, 60)
    
    return f"{hours:02d}:{minutes:02d}:{seconds:02d}"

# 运行测试
result = format_seconds(7385)
print(result)  # 输出示例: 02:03:05
Python divmod()函数:怎么同时获取商和余数
图 3:利用级联 divmod() 完成总秒数到时分秒的推导流

2. 分页处理与最后一页剩余量判定

在 Web 开发和接口编写中,分页是极其频繁的操作。根据总条目数和每页大小计算总页数时,如果存在余数,总页数需要加 1;同时余数直接告诉我们最后一页实际有几条数据。

def calculate_pagination(total_items: int, page_size: int):
    full_pages, remainder = divmod(total_items, page_size)
    
    # 若有剩余项,则总页数需要额外加 1 页
    total_pages = full_pages + (1 if remainder > 0 else 0)
    
    return {
        "total_pages": total_pages,
        "full_pages": full_pages,
        "last_page_items": remainder if remainder > 0 else page_size
    }

# 示例:105 条数据,每页 20 条
info = calculate_pagination(105, 20)
print(info)
# 输出: {'total_pages': 6, 'full_pages': 5, 'last_page_items': 5}

3. 进制转换与数位提取

在算法题或底层协议解析中,把十进制数转换为二进制、八进制或十六进制字符串,核心也是不断对目标进制基数取商和余数,直到商为 0 为止:

def decimal_to_base(num: int, base: int) -> str:
    if num == 0:
        return "0"
    digits = []
    while num > 0:
        num, rem = divmod(num, base)
        digits.append(str(rem))
    return "".join(reversed(digits))

print(decimal_to_base(25, 2))  # 输出二进制: 11001

四、细节与边界:负数向下取整与浮点数行为

1. 负数除法的向下取整规则

在处理包含负数的除法时,Python 的行为可能让习惯了 C/C++ 或 Java 的开发者感到意外。C 语言和 Java 采用的是“向零截断”(truncate towards zero),而 Python 严格采用“向下取整”(floor division,即向负无穷方向取整)。

例如计算 divmod(-17, 5):

  • 在浮点数概念中,-17 / 5 = -3.4;
  • 向负无穷方向取整得到的商是 -4(而不是 -3);
  • 根据恒等式 a == q * b + r,可得 -17 == (-4) * 5 + r,解得 r = 3。
q, r = divmod(-17, 5)
print(f"商: {q}, 余数: {r}")  # 输出: 商: -4, 余数: 3

在 Python 中,当除数 b > 0 时,余数 r 保证始终落在 [0, b-1] 区间内,余数永远不会是负数。余数的符号始终与除数保持一致。

2. 浮点数的 divmod()

divmod() 不仅支持整数,同样接受浮点数参数。当参数包含浮点数时,商的数值等于 math.floor(a / b),并以浮点类型返回:

q, r = divmod(7.5, 2.5)
print(q, r)  # 输出: 3.0 0.0

q2, r2 = divmod(8.7, 2.0)
print(q2, r2)  # 输出: 4.0 0.7000000000000002

注意精度陷阱:由于计算机底层基于 IEEE 754 标准表示双精度浮点数,浮点除法可能出现微小的精度误差(如上述输出中的 0.7000000000000002)。如果你在金融交易等对小数位数要求极其精确的场景下工作,建议优先使用 decimal.Decimal 模块配合相应的量化方法,而非直接依赖原生浮点数。

3. 异常与类型约束

  • 零除异常(ZeroDivisionError):如果第二个参数 b 为 0 或 0.0,divmod() 会直接抛出 ZeroDivisionError: integer division or modulo by zero,必须在调用前保证除数非零或做好 try-except 捕获。
  • 复数限制(TypeError):Python 中的复数类型(complex)不支持整除和取模运算,因此尝试执行 divmod(3+4j, 2) 会抛出 TypeError: can't take floor or mod of complex number.。
Python divmod()函数:怎么同时获取商和余数
图 4:divmod() 在不同参数类型下的计算表现与边界处理速查

五、代码选用指南与总结

为了让读者在实际编写代码时能更清晰地做选择,下表整理了不同场景下的推荐方案:

业务需求场景 推荐编写方式 选择理由
仅需要整除的商(如只判断能容纳几整批) q = a // b 意图直观单一,无需接收并忽略不需要的余数。
仅需要余数(如判断奇偶数、轮询取模) r = a % b 标准取模操作,可读性更强。
既要商又要余数(如时间转换、分页汇总、找零装箱) q, r = divmod(a, b) 单次调用完成解包,避免重复传参,执行效率更高,代码更精炼。
涉及海量大整数计算的加密与数论算法 q, r = divmod(a, b) 避免大整数除法的二次硬件级运算开销。

掌握 divmod() 的精髓在于识别“需要同时消费商和余数”的业务逻辑。通过一行简洁的解包赋值,不仅可以让代码少写一行冗余计算,更能向读代码的人清晰传递“商和余数来自同一次整体切分”的业务语义。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表