本系列作为本人@takooctopus深入学习python机制的记录,这个博客遵照着栖迟于一丘的博客上面的流程进行的,也包含我在实际查看源码时的感想,特此列出,表示感谢。
关于Python2 和 Python3
由于python2考虑了32位数,其分为了int和long,其中int会考虑溢出问题。我们在python3中对这个问题进行了修正,全部都使用了长整型。
我们来看一看这个类的实现:
{{- code -}}
我们将其中的属性做个表格
属性元信息 | 解释 |
---|---|
long_dealloc | 对象的析构操作 |
PyObject_Del | 对象的释放操作 |
long_to_demical_string | 转化为PyString对象 |
long_hash | 获取hash值 |
long_richcompare | 比较 |
long_as_number | 数值比较 |
long_methods | 成员函数 |
就在同一个文件[longobject.c]
中,我们可以看见对其属性的具体定义,以属性比较long_richcompare()
为例:
{{- code -}}
我们可以看见其比较两个整型对象大小直接使用了==
运算符,当两个直接引用的同一个对象可以快速的判断。而在其他的情况下会引用long_compare()
函数。
long_compare()
函数也定义在[longobject.c]
中
{{- code -}}
这个比较第一步就是比较了两个PyObject
的大小,最终通过sigh
输出相对大小。
在位数相同时,依次比较各个位数。
但我们要明确一点,就是长整型long
在python中是以int[]
的柔性数组实现的。,其数组名为ob_digit[]
。
我们回看[Include/longintrepr.h]
中
{{- code -}}
我们看其注释:
- 整数绝对值为
SUM(for i=0 through abs(ob_size)-1) ob_digit[i] * 2**(SHIFT*i)
- 其正负用
ob_size
的正负表示 - 0值使用
ob_size == 0
- 正常的整数
ob_digit[abs(ob_size)-1]
不为0 - 对于所有有效的
i
,其ob_digit[i]
范围在[0,MASK]
之间
我们简单的表示一下,当ob_size=3
,ob_digit=[4,2,1]
,SHIFT=30
,MASK=2^30-1
时:
\begin{align}
digit &= 4*{( 2^{ 30 } )}^0 + 2*{( 2^{ 30 } )}^1 + 1*{( 2^{ 30 } )}^2 \\
&= 1152921506754330628
\end{align}
可以看出是一个进制转换过程。
作为一个数值对象,我们首先得实现它的数值运算操作long_as_number
:
{{- code -}}
加法运算
我们不妨从最基础的加法看起:
{{- code -}}
更为具体的实现在x_add()
和x_sub()
中:
{{- code -}}
我们可以看见每步都使用了掩码,我们去找PyLong_MASK
和PyLong_SHIFT
的具体值。
{{- code -}}
PyLong_SHIFT
应该为5的倍数。
PyLong_MASK
的值为0b111111111111111111111111111111
「30个1」
我们在此能看见python保存整型中,ob_digit[]
中位数为30位,略小于理论上int
的32位。「在有些版本是15位,不知道现在30位的好处在什么」
carry
作为了一般的进位处理和中间结果。
z
作为最终的结果变量,但最开始申请空间时默认比两个加数的空间大一,最终需要进行空间的整理long_normalize()
:
{{- code -}}
Py_size
在[Include/object.h]
中用宏定义
{{- code -}}
即通用地调整了数组的ob_size
。
乘法运算
同样的,我们来看乘法相关运算:
乘法运算同样定义在[Objects/longobject.c]
{{- code -}}
我们看当两个数的乘积能用long long
类型保存时,即ob_size
均小于1时,使用简单的两数相乘。再将其转化为PyLong_Type
。如果整数过大,就使用k_mul
计算,采用Karatsuba multiplication算法。
{{- code -}}
上面是简单的实现,具体的可以看wikipedia的介绍或者我相关的博文
小整数
特别的,在整数范围内,有一些整数被视为小整数NSMALLINTS
,其范围在[-5,257)
中,保存于small_ints[]
这个数组中。
其最主要的是,在小整数池中的数,都会给出同一个引用。
{{- code -}}
特别的,对象池初始化在_PyLong_Init()
函数中:
{{- code -}}