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

最后修改: 2025-02-01 08:59:27 GMT