PEP 791 – math.integer — 整数专属数学函数子模块
- 作者:
- Sergey B Kirpichev <skirpichev at gmail.com>
- 发起人:
- Victor Stinner <vstinner at python.org>
- 讨论至:
- Discourse 帖子
- 状态:
- 草案
- 类型:
- 标准跟踪
- 创建日期:
- 2025年5月12日
- Python 版本:
- 3.15
- 发布历史:
- 2018年7月12日, 2025年5月9日, 2025年5月19日
摘要
本PEP提议为数论、组合学和其他定义为整数参数的函数(如 math.gcd()
或 math.isqrt()
)创建一个新的子模块。
动机
math
文档中写道:“此模块提供对C标准定义的数学函数的访问。”但是,随着时间的推移,该模块被填充了与C标准或浮点算术无关的函数。现在,要描述模块的范围、内容和接口(返回值或接受的参数)变得更加困难。
例如,文档中的以下声明:“除非另有明确说明,所有返回值都是浮点数。”这已不再正确:文档的数论函数部分中列出的函数都没有返回浮点数,但文档没有说明。在提议的 math.integer
子模块的文档中,“所有返回值都是整数”这句话将是准确的。以类似的方式,我们可以简化新子模块和 math
中函数的接受参数的描述。
现在,满足人们对模块内容的期望变得更加困难。例如,他们是否应该期望 math.factorial(100)
返回一个精确的结果?许多语言、Python包(如 scipy)或袖珍计算器都有相同或相似名称的函数,它们返回一个浮点值,在这个例子中只是一个近似值。
显然,math
模块不能作为数学函数的万能库,因为我们还有 cmath
和 statistics
模块。让我们为整数相关函数也这样做。它提供共享上下文,减少文档的冗长性和概念负担。它还通过分组相关函数来帮助发现性,并使IDE(例如新的CPython的REPL)建议更有用。
目前,CPython中 math
模块的代码约为4200行,其中新模块的代码大约占1/3(1300行)。这与 cmath
(1340行)相当,而 cmath
不是 libm
的简单包装器,就像 math
模块中的大多数函数一样。
而且这种情况有恶化的趋势。当模块拆分首次被提出时,只有两个整数相关函数: factorial()
(也接受 float
,像模块中的其他函数一样)和 gcd()
(从 fractions
模块移出)。随后添加了 isqrt()
、 comb()
和 perm()
,并第二次提议添加新模块,以便所有新函数都直接进入其中,而不会污染 math
命名空间。现在有六个函数,而且 factorial()
不再接受 float
。
在最初的讨论串和问题 python/cpython#81313 中提出的一些可能的添加包括:
c_div()
和n_div()
— 用于整数除法,分别向上(向正无穷大)取整和向最近整数取整,参见相关讨论帖。这在标准库中多次被重新发明,例如在datetime
和fractions
中。而且正如讨论帖所演示的,这很容易出错。gcdext()
— 解决两个变量的线性丢番图方程(int
实现实际上包含扩展欧几里得算法)isqrt_rem()
— 返回整数平方根和余数(仅当整数不是完全平方数时余数才非零)ilog()
— 整数对数,math.log()
对整数参数有特殊处理。它(相对于其他模块函数)是独特的,并且迄今为止没有文档说明,参见问题 python/cpython#120950。fibonacci()
— 斐波那契数列。
独立的命名空间消除了与现有 math
模块函数可能存在的名称冲突。例如,用于整数向上取整除法的可能名称 ceil_div()
或 ceildiv()
将与 ceil()
(用于 float
,有时碰巧对整数除法做出了正确的事情——但通常不会)发生冲突。
基本原理
这都是关于文档的问题吗?为什么不直接修复它呢?不,不是。当然,我们可以在模块前言中更模糊一些(例如,大致说“math
模块包含一些数学函数”),我们可以准确描述每个函数的输入/输出及其行为(例如,factorial()
的输出是否精确,如 scipy.special.factorial,默认情况下)。
但主要问题是当前模块混合了不同、几乎不交织的应用领域。添加更多文档只会突出这一点,并使最终用户的问题更糟(需要阅读/跳过更多文本)。而且它不会解决可发现性问题(要知道在哪里找到函数,以及是否能找到,你需要查看模块中的所有函数),也不会解决Tab键自动完成问题。
规范
本PEP提议将以下整数相关函数移到一个新的子模块,命名为 math.integer
它们在 math
中的别名将被软弃用。本PEP不会引入向后不兼容的更改。
模块函数将接受整数和实现 __index__()
方法的对象,该方法用于将对象转换为整数。在有足够的时间和内存的情况下,合适的函数必须精确计算。
intmath 包将为旧版本的Python提供新的子模块内容。
可能的扩展
新函数(如动机部分所述)不属于此提案。
不过,我们应该提到,除非我们能直接绑定到一些受良好支持的数学库(如 GMP),否则子模块的范围应该受到限制。例如,不包括素数测试和因式分解,因为生产质量的实现需要贡献者具备良好的数学背景,并且更属于专业库。
当提议的函数已存在于 gmpy2 中时,我们应优先为标准库提供兼容的接口。
向后兼容性
由于 math
中的别名将无限期保留(不鼓励使用),因此预计不会有代码中断。
如何教授此内容
新子模块将是以下函数的存放之处:1) 接受类似 int
的参数并返回整数的函数,以及 2) 属于任意精度整数算术领域的函数,即不依赖于平台浮点格式或行为和/或平台数学库 (libm
) 的函数。
对于用户来说,首先查看 int
的方法是很自然的,这些方法涵盖了大多数基本用例(例如 int.bit_length()
方法),然后才是标准库中专门的模块。
参考实现
被拒绝的想法
isqrt() 重命名
曾有短暂的讨论,关于将 math.isqrt()
公开为 imath.sqrt
,就像 cmath.sqrt()
是 math.sqrt()
的复数版本一样。然而,isqrt
最终是一个不同的函数:它是平方根的下限。给它相同的名称(在不同的模块下)会造成混淆。
模块名称
投票结果显示 intmath
是最受欢迎的候选名称,imath
紧随其后。
其他提议的名称包括 ntheory
(如 SymPy 的子模块), integermath
, zmath
, dmath
和 imaths
。
但是指导委员会(SC)倾向于子模块而不是新的顶级模块。math
的子模块最受欢迎的变体是:integer
、discrete
或 ntheory
(作者偏好)。
致谢
感谢 Tim Peters 重新提出拆分 math
模块的建议。感谢 Neil Girdhar 对初稿的重大改进。
版权
本文档置于公共领域或 CC0-1.0-Universal 许可证下,以更宽松者为准。
来源:https://github.com/python/peps/blob/main/peps/pep-0791.rst