Python2 的整数对象 有 PyIntObject
和 PyLongObject
这两种类型,Python3 只保留了 PyLongObject
。 这里我们介绍一下python3的PyLongObject
。
1 2 3 4 5 6 7 8 9 10 Python 3.7 .3 (default, Mar 27 2019 , 22 :11 :17 >>> a = 45 >>> type (a)<class 'int '> # 小整数
PyLongObject 1 2 3 4 5 6 7 8 9 10 11 PyTypeObject PyLong_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0 ) "int" , offsetof(PyLongObject, ob_digit), sizeof (digit), long_dealloc, ... long_new, PyObject_Del, };
我们可以看到,PyLong_Type类型对象的tp_name就是int,也就是说,在Python内部,它就是int类型。
源文件:Include/longobject.h
源文件:Include/longintrepr.h
1 2 3 4 5 6 7 8 typedef struct _longobject PyLongObject ;struct _longobject { PyVarObject ob_base; digit ob_digit[1 ]; };
从源码可以看出 PyLongObject 是变长对象
类型对象 PyLong_Type 源文件:Objects/longobject.c
创建整数对象 从 PyLong_Type 可以看出,创建一个整数对象的入口函数为 long_new
源文件:Objects/clinic/longobject.c.h
从 long_new_impl 函数可以看出有如下几种情况
x == NULL 且 obase != NULL 调用 PyLong_FromLong
obase 为 NULL 调用 PyNumber_Long
x 和 obase 都不为 NULL
PyUnicode 调用 PyLong_FromUnicodeObject,最终调用 PyLong_FromString
PyByteArray/PyBytes 调用_PyLong_FromBytes,最终调用 PyLong_FromString
小整数对象 一些整数在一开始就会被初始化一直留存,当再次使用直接从小整数对象池中获取,不用频繁的申请内存。
默认的小整数范围是 [-5, 257) 源文件:Objects/longobject.c
get_small_int
宏 CHECK_SMALL_INT 会检查传入的数是否在小整数范围内,如果是直接返回。 可以在创建或复制整数对象等函数中找到 CHECK_SMALL_INT 的身影,以下只列出了PyLong_FromLong ,就不一一列举了
小整数初始化 源文件:Objects/longobject.c
_PyLong_Init
整数的存储结构 源文件:Objects/longobject.c
在 long_to_decimal_string_internal 中添加如下代码并重新编译安装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 static int long_to_decimal_string_internal (PyObject *aa, PyObject **p_output, _PyUnicodeWriter *writer, _PyBytesWriter *bytes_writer, char **bytes_str) { PyLongObject *scratch, *a; PyObject *str = NULL ; Py_ssize_t size, strlen , size_a, i, j; digit *pout, *pin, rem, tenpow; int negative; int d; enum PyUnicode_Kind kind ; a = (PyLongObject *)aa; printf ("ob_size = %d\n" , Py_SIZE(a)); for (int index = 0 ; index < Py_SIZE(a); ++index) { printf ("ob_digit[%d] = %d\n" , index, a->ob_digit[index]); } ... }
编译安装后进入 python 解释器输入如下代码
1 2 3 4 5 6 7 8 9 num = 9223372043297226753 print (num)>>> ob_size = 3 >>> ob_digit[0 ] = 1 >>> ob_digit[1 ] = 6 >>> ob_digit[2 ] = 8 >>> 9223372043297226753
如下图所示
注:这里的 30 是由 PyLong_SHIFT 决定的,64 位系统中,PyLong_SHIFT 为 30,否则 PyLong_SHIFT 为 15
整数对象的数值操作 整数相加 源文件:Objects/longobject.c
long_add
可以看到整数的加法运算函数 long_add 根据 a、b 的 ob_size 又细分为两个函数 (x_add 和 x_sub) 做处理
源文件:Objects/longobject.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 static PyLongObject *x_add (PyLongObject *a, PyLongObject *b) { Py_ssize_t size_a = Py_ABS(Py_SIZE(a)), size_b = Py_ABS(Py_SIZE(b)); PyLongObject *z; Py_ssize_t i; digit carry = 0 ; if (size_a < size_b) { { PyLongObject *temp = a; a = b; b = temp; } { Py_ssize_t size_temp = size_a; size_a = size_b; size_b = size_temp; } } z = _PyLong_New(size_a+1 ); if (z == NULL ) return NULL ; for (i = 0 ; i < size_b; ++i) { carry += a->ob_digit[i] + b->ob_digit[i]; z->ob_digit[i] = carry & PyLong_MASK; carry >>= PyLong_SHIFT; } for (; i < size_a; ++i) { carry += a->ob_digit[i]; z->ob_digit[i] = carry & PyLong_MASK; carry >>= PyLong_SHIFT; } z->ob_digit[i] = carry; return long_normalize(z); }
加法运算函数 x_add 从 ob_digit 数组的低位开始依次按位相加,carry 做进位处理,然后处理 a 对象的高位数字,最后使用 long_normalize 函数调整 ob_size,确保 ob_digit[abs(ob_size)-1]不为零,这与普通四则运算的加法运算相同,只不过进位单元不同而已
源文件:Objects/longobject.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 static PyLongObject *x_sub (PyLongObject *a, PyLongObject *b) { Py_ssize_t size_a = Py_ABS(Py_SIZE(a)), size_b = Py_ABS(Py_SIZE(b)); PyLongObject *z; Py_ssize_t i; int sign = 1 ; digit borrow = 0 ; if (size_a < size_b) { sign = -1 ; { PyLongObject *temp = a; a = b; b = temp; } { Py_ssize_t size_temp = size_a; size_a = size_b; size_b = size_temp; } } else if (size_a == size_b) { i = size_a; while (--i >= 0 && a->ob_digit[i] == b->ob_digit[i]) ; if (i < 0 ) return (PyLongObject *)PyLong_FromLong(0 ); if (a->ob_digit[i] < b->ob_digit[i]) { sign = -1 ; { PyLongObject *temp = a; a = b; b = temp; } } size_a = size_b = i+1 ; } z = _PyLong_New(size_a); if (z == NULL ) return NULL ; for (i = 0 ; i < size_b; ++i) { borrow = a->ob_digit[i] - b->ob_digit[i] - borrow; z->ob_digit[i] = borrow & PyLong_MASK; borrow >>= PyLong_SHIFT; borrow &= 1 ; } for (; i < size_a; ++i) { borrow = a->ob_digit[i] - borrow; z->ob_digit[i] = borrow & PyLong_MASK; borrow >>= PyLong_SHIFT; borrow &= 1 ; } assert(borrow == 0 ); if (sign < 0 ) { Py_SIZE(z) = -Py_SIZE(z); } return long_normalize(z); }
与普通四则运算减法相同,数不够大则向高一位借位, 减法运算函数 x_sub 的示例图如下,注:PyLong_SHIFT 为 30
整数相乘 源文件:Objects/longobject.c
long_mul
k_mul 函数是一种快速乘法 源文件
Karatsuba 的算法主要是用于两个大数的乘法,极大提高了运算效率,相较于普通乘法降低了复杂度,并在其中运用了递归的思想。 基本的原理和做法是将位数很多的两个大数 x 和 y 分成位数较少的数,每个数都是原来 x 和 y 位数的一半。 这样处理之后,简化为做三次乘法,并附带少量的加法操作和移位操作。
具体可以看 wiki Karatsuba 算法 的实现
其他 在 Objects/longobject.c 的第三行有这么一句话 XXX The functional organization of this file is terrible
, 而且这个注释在Python2.7版本中也有,并一直保留到最新版本master分支,看来是一段陈年往事。