PEP 237 – 统一长整型和整型
- 作者:
- Moshe Zadka, Guido van Rossum
- 状态:
- 最终版
- 类型:
- 标准跟踪
- 创建日期:
- 2001年3月11日
- Python 版本:
- 2.2
- 发布历史:
- 2001年3月16日,2001年8月14日,2001年8月23日
摘要
Python 目前区分两种整数(int):常规或短整型,受 C long 大小限制(通常为 32 或 64 位),以及长整型,仅受可用内存限制。当短整型运算结果超出 C long 范围时,它们会引发错误。此外还有一些其他区别。本 PEP 提议消除大多数语义差异,从 Python 用户的角度统一这两种类型。
基本原理
许多程序在事后发现需要处理更大的数字,而之后更改算法会很麻烦。在所有算术运算都使用长整型(无论是否需要)的正常情况下,这会影响性能。
将机器字长暴露给语言会阻碍可移植性。例如,由于这个原因,Python 源文件和 .pyc 文件在 32 位和 64 位机器之间不可移植。
还有一个普遍的愿望是,在不相关的应用中,对 Python 用户隐藏不必要的细节。一个例子是内存分配,它在 C 中是显式的,但在 Python 中是自动的,这给我们带来了字符串、列表等无限大小的便利。将这种便利扩展到数字是合理的。
这将使新的 Python 程序员(无论他们是否是编程新手)在开始使用该语言之前少学一件事。
实施
最初,提出了两种替代实现方案(每位作者一种)
PyInt类型的 C long 槽将被转换为union { long i; struct { unsigned long length; digit digits[1]; } bignum; };
long的只有n-1个低位有意义;最高位总是设置的。这区分了union。所有PyInt函数在决定使用哪种操作类型之前都会检查这个位。- 现有的短整型和长整型保持不变,但当结果无法表示为短整型时,操作会返回一个长整型而不是引发
OverflowError。可能会引入一个新类型integer,它是一个抽象基类型,int和long实现类型都是它的子类。这很有用,因为程序可以通过一次测试来检查整数性if isinstance(i, integer): ...
经过一番考虑,选择了第二种实现方案,因为它更容易实现,在 C API 级别上向后兼容,此外还可以作为过渡措施部分实现。
不兼容性
以下操作对短整型和长整型具有(通常是微妙的)不同语义,其中一个或另一个必须以某种方式更改。这是一个详尽的列表。如果您知道任何其他操作在传递具有相同值的短整型或长整型时结果不同的,请联系第二作者。
- 目前,除了
<<之外的所有短整型算术运算符,如果结果不能表示为短整型,都会引发OverflowError。这将被更改为返回长整型。以下运算符目前可能引发OverflowError:x+y、x-y、x*y、x**y、divmod(x, y)、x/y、x%y和-x。(最后四个只有在涉及值-sys.maxint-1时才会溢出。) - 目前,
x<<n可能会丢失短整型的位。如果返回短整型会丢失位(其中符号改变被视为丢失位的特殊情况),这将更改为返回包含所有移出位的长整型。 - 目前,短整型的十六进制和八进制字面量可以指定负值;例如,在 32 位机器上
0xffffffff == -1。这将更改为等于0xffffffffL(2**32-1)。 - 目前,
%u、%x、%X和%o字符串格式化运算符以及hex()和oct()内置函数对负数的行为不同:负短整型被格式化为无符号 C long,而负长整型则格式化为带负号。这将更改为在所有情况下都使用长整型语义(但没有目前区分hex()和oct()输出的长整型的末尾 L)。请注意,这意味着%u成为%d的别名。它最终将被删除。 - 目前,长整型的
repr()返回一个以 L 结尾的字符串,而短整型的repr()则不返回。L 将被删除;但不会在 Python 3.0 之前。 - 目前,带有长操作数的运算永远不会返回短整型。这 可能 会改变,因为它允许一些优化。(尚未在此领域进行任何更改,也没有任何计划。)
- 表达式
type(x).__name__取决于 x 是短整型还是长整型。由于选择了实现方案 2,这种差异将保留。(在 Python 3.0 中,我们 可能 能够使用一个技巧来隐藏这种差异,因为它 确实 令人烦恼地向用户代码揭示这种差异,而且随着两种类型之间差异越来越不明显,这种烦恼会加剧。) - 长整型和短整型由
marshal模块以及pickle和cPickle模块以不同的方式处理。这种差异将保留(至少直到 Python 3.0)。 - 具有小值(通常在 -1 到 99 之间,包含边界)的短整型是 interned 的——每当结果具有这样的值时,就会返回一个具有相同值的现有短整型。对于具有相同值的长整型,则不这样做。这种差异将保留。(由于不保证这种 interning,因此这种差异是否是语义差异值得商榷——但可能存在使用
is进行短整型比较的代码,并且由于这种 interning 而恰好有效。此类代码如果与长整型一起使用可能会失败。)
字面量
整数文字末尾的 L 将不再有任何意义,并最终变为非法。编译器将完全根据值选择适当的类型。(在 Python 3.0 之前,它将强制文字为长整型;但没有末尾 L 的文字也可能是长整型,如果它们不能表示为短整型。)
内置函数
函数 int() 将根据参数值返回短整型或长整型。在 Python 3.0 中,函数 long() 将调用函数 int();在此之前,它将继续强制结果为长整型,但其他方面与 int() 相同。内置名称 long 将保留在语言中以表示长整型实现类型(除非它在 Python 3.0 中完全根除),但仍然建议使用 int() 函数,因为它在需要时会自动返回长整型。
C API
C API 保持不变;C 代码仍需了解短整型和长整型之间的差异。(Python 3.0 C API 可能会完全不兼容。)
PyArg_Parse*() API 已经接受长整型,只要它们在 C int 或 long 可表示的范围内,这样接受 C int 或 long 参数的函数就不必担心处理 Python long 了。
过渡
过渡分为三个主要阶段
- 目前会引发
OverflowError的短整型操作会返回长整型值。这是此阶段唯一的变化。字面量仍将区分短整型和长整型。上面列出的其他语义差异(包括<<的行为)将保留。由于此阶段只改变目前会引发OverflowError的情况,因此假设这不会破坏现有代码。(依赖此异常的代码会过于复杂而无需关注。)对于那些关注极端向后兼容性的人,命令行选项(或调用 warnings 模块)将允许在此点发出警告或错误,但默认情况下是关闭的。 - 处理其余的语义差异。在所有情况下,长整型语义将占主导地位。由于这将引入向后不兼容性,可能会破坏一些旧代码,因此此阶段可能需要未来的声明和/或警告,以及一个漫长的过渡阶段。尾随的 L 将继续用于长整型的输入和
repr()的输出。- 启用对在阶段 2B 中将改变其数字结果的操作的警告,特别是
hex()和oct(),%u、%x、%X和%o,以及在(包含边界的)范围[sys.maxint+1, sys.maxint*2+1]中的hex和oct字面量,以及丢失位的左移操作。 - 这些操作的新语义已实现。给出与以前不同结果的操作将 不会 发出警告。
- 启用对在阶段 2B 中将改变其数字结果的操作的警告,特别是
- 尾随的 L 从
repr()中删除,并在输入时变为非法。(如果可能,long类型将完全消失。)尾随的 L 也从hex()和oct()中删除。
阶段 1 将在 Python 2.2 中实现。
阶段 2 将逐步实现,2A 在 Python 2.3 中,2B 在 Python 2.4 中。
阶段 3 将在 Python 3.0 中实现(在 Python 2.4 发布后至少两年)。
溢出警告
以下是目前会引发 OverflowError 的情况下生成警告的规则。这适用于过渡阶段 1。历史备注:尽管阶段 1 在 Python 2.2 中完成,阶段 2A 在 Python 2.3 中完成,但没有人注意到 Python 2.3 中仍然生成 OverflowWarning。它最终在 Python 2.4 中被禁用。Python 内置的 OverflowWarning 以及相应的 C API PyExc_OverflowWarning 在 Python 2.4 中不再生成或使用,但将保留到 Python 2.5(以防用户代码使用,尽管可能性不大)。
- 引入了一个新的警告类别,
OverflowWarning。这是一个内置名称。 - 如果一个 int 结果溢出,将发出一个
OverflowWarning警告,并带有一个消息参数指示操作,例如“整数加法”。这可能会或可能不会在sys.stderr上显示警告消息,或者可能引发异常,所有这些都由-W命令行和 warnings 模块控制。 OverflowWarning警告默认被忽略。OverflowWarning警告可以像所有警告一样,通过-W命令行选项或通过warnings.filterwarnings()调用来控制。例如python -Wdefault::OverflowWarning
使
OverflowWarning在特定源行首次出现时显示,并且python -Werror::OverflowWarning
使
OverflowWarning在发生时总是转换为异常。以下代码在程序内部启用警告import warnings warnings.filterwarnings("default", "", OverflowWarning)
请参阅 Python
man页面中关于-W选项的内容,以及warnings模块文档中关于filterwarnings()的内容。- 如果
OverflowWarning警告转换为错误,则会替换为OverflowError。这是为了向后兼容性所需的。 - 除非警告被转换为异常,否则操作的结果(例如
x+y)将在将参数转换为长整型后重新计算。
示例
如果您将长整型传递给接受整数的 C 函数或内置操作,只要该值符合要求(通过 PyArg_ParseTuple() 的实现方式),它将被视为与短整型相同。如果长整型值不符合要求,它仍将引发 OverflowError。例如
def fact(n):
if n <= 1:
return 1
return n*fact(n-1)
A = "ABCDEFGHIJKLMNOPQ"
n = input("Gimme an int: ")
print A[fact(n)%17]
对于 n >= 13,这目前会引发 OverflowError(除非用户在输入中输入了尾随的 L),即使计算出的索引始终在 range(17) 中。采用新方法后,此代码将正确执行:索引将计算为长整型,但其值将在范围内。
已解决的问题
这些以前悬而未决的问题已得到解决。
- 应用于长整型的
hex()和oct()将继续生成尾随的 L,直到 Python 3000。上面的原文对此不清楚,但由于 Python 2.4 中没有发生这种情况,所以认为最好不要动它。BDFL 的声明在此https://mail.python.org/pipermail/python-dev/2006-June/065918.html
sys.maxint该怎么办?保留它,因为它在短整型和长整型之间的区别仍然相关时(例如,在检查值的类型时)仍然相关。- 我们应该完全删除
%u吗?删除它。 - 我们应该警告
<<不截断整数吗?是的。 - 溢出警告应该基于可移植的最大大小吗?不。
实施
Python 2.x 系列的实现工作已完成;阶段 1 已随 Python 2.2 发布,阶段 2A 随 Python 2.3 发布,阶段 2B 将随 Python 2.4 发布(并且已经在 CVS 中)。
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0237.rst