Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 303 – 为多个除数扩展 divmod()

作者:
Thomas Bellman <bellman+pep-divmod at lysator.liu.se>
状态:
已拒绝
类型:
标准跟踪
创建:
2002-12-31
Python 版本:
2.3
历史记录:


目录

摘要

本 PEP 描述了对内置 divmod() 函数的扩展,允许它接受多个除数,将对 divmod() 的多次调用链接到一起。

宣告

本 PEP 已被拒绝。大多数对链接 divmod() 的使用都涉及到一个常数模数(例如在基数转换中),并且更适合用循环进行编码。将秒分解为天/小时/分钟/秒的示例不能推广到月和年;相反,整个用例通过日期和时间模块得到更灵活和稳健的处理。PEP 中提到的其他用例在实际代码中比较罕见。该提案在清晰度和明显性方面也存在问题。在示例中,并不立即清楚参数顺序是否正确,或者目标元组的长度是否正确。来自其他语言的用户更有可能理解标准的两个参数形式,而无需重新阅读文档。参见 2005 年 6 月 17 日的 python-dev 讨论 [1]

规范

内置 divmod() 函数将被修改为接受多个除数,将其签名从 divmod(dividend, divisor) 更改为 divmod(dividend, *divisors)。被除数除以最后一个除数,得到商和余数。然后将商除以倒数第二个除数,得到新的商和余数。重复此过程,直到所有除数都被使用,然后 divmod() 返回一个元组,该元组包含最后一步的商和所有步骤的余数。

divmod() 行为的 Python 实现可能如下所示

def divmod(dividend, *divisors):
    modulos = ()
    q = dividend
    while divisors:
        q, r = q.__divmod__(divisors[-1])
        modulos = (r,) + modulos
        divisors = divisors[:-1]
    return (q,) + modulos

动机

有时需要执行一系列 divmod() 操作,对前一步的商调用 divmod(),并使用不同的除数。最常见的用例可能是将秒数转换为周、天、小时、分钟和秒。今天这将被写成

def secs_to_wdhms(seconds):
    m, s = divmod(seconds, 60)
    h, m = divmod(m, 60)
    d, h = divmod(h, 24)
    w, d = divmod(d, 7)
    return (w, d, h, m, s)

这很繁琐,并且每次需要它时都很容易出错。

如果按照提案更改 divmod() 内置函数,则将秒数转换为周、天、小时、分钟和秒的代码将变为

def secs_to_wdhms(seconds):
    w, d, h, m, s = divmod(seconds, 7, 24, 60, 60)
    return (w, d, h, m, s)

这更易于键入,更易于正确键入,更易于阅读。

其他应用是

  • 天文角度(赤纬以度、分、秒计量,赤经以小时、分、秒计量)。
  • 旧的英国货币(1 英镑 = 20 先令,1 先令 = 12 便士)。
  • 盎格鲁撒克逊长度单位:1 英里 = 1760 码,1 码 = 3 英尺,1 英尺 = 12 英寸。
  • 盎格鲁撒克逊重量单位:1 长吨 = 160 石,1 石 = 14 磅,1 磅 = 16 盎司,1 盎司 = 16 克朗。
  • 英国体积:1 加仑 = 4夸脱,1 夸脱 = 2 品脱,1 品脱 = 20 液盎司。

理由

这个想法来自 APL,它有一个执行此操作的运算符。(我不记得运算符是什么样子,而且可能无法用 ASCII 渲染。)

APL 运算符以列表作为其第二个操作数,而本 PEP 建议每个除数应该是 divmod() 函数的单独参数。这主要是为了预期最常见的用途将除数作为常数直接放在调用中(如上面的 7、24、60、60),添加一组括号或方括号只会使调用变得混乱。

要求将显式序列作为 divmod() 的第二个参数将严重破坏向后兼容性。让 divmod() 检查其第二个参数是否为序列被认为过于丑陋,无法考虑。而在需要使用从其他地方计算得到的序列的情况下,很容易编写 divmod(x, *divs) 来代替。

已经考虑过要求至少有一个除数,即拒绝 divmod(x),但没有想到任何充分的理由这样做,因此为了通用性而允许这样做。

不带除数调用 divmod() 仍然应该返回一个元组(包含一个元素)。调用 divmod() 时除数个数可变,因此返回值具有“未知”元素个数,否则需要对这种情况进行特殊处理。调用 divmod()知道没有除数的代码被认为太愚蠢,不值得进行特殊处理。

已经考虑过以相反的方向处理除数,即先用第一个除数除,而不是先用最后一个除数除。但是,结果以最高有效位开头,最低有效位结尾(将链接的 divmod 视为一种将数字拆分为“数字”的方法,具有不同的权重),并且有理由以与结果相同的顺序指定除数(权重)。

逆运算

def inverse_divmod(seq, *factors):
    product = seq[0]
    for x, y in zip(factors, seq[1:]):
        product = product * x + y
    return product

也可能有用。但是,编写

seconds = (((((w * 7) + d) * 24 + h) * 60 + m) * 60 + s)

比链接的 divmod 更容易编写和阅读。因此,它被认为不太重要,它的引入可以推迟到它自己的 PEP。此外,这样的函数需要一个好名字,而 PEP 作者还没有想出一个好名字。

调用 divmod("spam") 不会引发错误,尽管字符串既不支持除法也不支持模运算。但是,除非我们也知道另一个对象,否则我们无法确定 divmod() 是否有效,因此禁止它似乎很愚蠢。

向后兼容性

任何在 __builtin__ 模块中替换 divmod() 函数的模块都可能导致使用新语法的其他模块中断。预计这种情况非常罕见。

当使用除两个参数以外的任何参数调用 divmod() 时,期望出现 TypeError 异常的代码将中断。这也预计非常罕见。

目前没有发现其他关于向后兼容性的问题。

参考实现

尚未完成,但似乎在 Python/bltinmodule.c 中对 builtin_divmod() 函数进行了相当简单的重新实现。

参考资料


来源:https://github.com/python/peps/blob/main/peps/pep-0303.rst

最后修改时间:2023-09-09 17:39:29 GMT