PyLongObject
PyLongObject
同样的,通过宏定义,PyLongObject是_longobject的别名,在python中数字对象是变长对象,由于这个原因,我们可以表示一个极大的数,而不用考虑溢出。
struct _longobject {
PyObject_VAR_HEAD
digit ob_digit[1];
};
- PyObject_VAR_HEAD为object.h中定义的变长对象头部。
- 由代码可知,int对象是通过整数数组来实现大整数的。一个C整数类型不够就可以使用两个,直到n个。
Python提供了两个版本的整数类型来实现整数类型,一个是32位的uint32_t,一个是16位的unsigned short,编译Python解析器时可以通过宏定义指定选用的版本。
#if PYLONG_BITS_IN_DIGIT == 30 typedef uint32_t digit; typedef int32_t sdigit; /* signed variant of digit */ typedef uint64_t twodigits; typedef int64_t stwodigits; /* signed variant of twodigits */ #define PyLong_SHIFT 30 #define _PyLong_DECIMAL_SHIFT 9 /* max(e such that 10**e fits in a digit) */ #define _PyLong_DECIMAL_BASE ((digit)1000000000) /* 10 ** DECIMAL_SHIFT */ #elif PYLONG_BITS_IN_DIGIT == 15 typedef unsigned short digit; typedef short sdigit; /* signed variant of digit */ typedef unsigned long twodigits; typedef long stwodigits; /* signed variant of twodigits */ #define PyLong_SHIFT 15 #define _PyLong_DECIMAL_SHIFT 4 /* max(e such that 10**e fits in a digit) */ #define _PyLong_DECIMAL_BASE ((digit)10000) /* 10 ** DECIMAL_SHIFT */ #else #error "PYLONG_BITS_IN_DIGIT should be 15 or 30" #endif
- 由于Python提供的两个版本的整数类型都是无符号整型,负数的表示就交给了ob_size字段,如果是负的长度,则代表着负数。
- 由于是整数数组形式表示数字,数字与数字的运算就变的复杂,python中有实现整数数组数学运算的基本函数。
PyLong_Type
在PyLong_Type中可以发现,PyLong_Type对象的类型为PyType_Type,对象名称为int,需要开辟的内存大小都在定义了,long_as_number里定义了对象的一些基本方法。
PyTypeObject PyLong_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"int", /* tp_name */
offsetof(PyLongObject, ob_digit), /* tp_basicsize */
sizeof(digit), /* tp_itemsize */
... /* tp_iternext */
&long_as_number,
...
};
long_as_number中包含的部分方法如下,都是基础操作
static PyNumberMethods long_as_number = {
(binaryfunc)long_add, /*nb_add*/
(binaryfunc)long_sub, /*nb_subtract*/
(binaryfunc)long_mul, /*nb_multiply*/
long_mod, /*nb_remainder*/
long_divmod, /*nb_divmod*/
long_pow, /*nb_power*/
(unaryfunc)long_neg, /*nb_negative*/
long_long, /*tp_positive*/
(unaryfunc)long_abs, /*tp_absolute*/
(inquiry)long_bool, /*tp_bool*/
(unaryfunc)long_invert, /*nb_invert*/
long_lshift, /*nb_lshift*/
long_rshift, /*nb_rshift*/
long_and, /*nb_and*/
long_xor, /*nb_xor*/
long_or, /*nb_or*/
long_long, /*nb_int*/
...
};
整数池
小整数对象:在python中,小整数是经常频繁使用的数字,在一次range循环时就会大量使用,如果在运行时再动态的创建他们,则python的效率则会非常底下,python不会容忍这一点,所以python将[-5, 256]的小整数放入缓存池中,需要时直接去取,不使用时,放入整数池,则不需要重新创建,重新分配空间。[-5, 256]这个数字是可以修改的,不过是从c语言层面修改,修改后需要重新编译工程文件。
// \Python-3.10.10\Include\internal\pycore_interp.h #define _PY_NSMALLPOSINTS 257 #define _PY_NSMALLNEGINTS 5 // \Python-3.10.10\Objects\longobject.c #define NSMALLNEGINTS _PY_NSMALLNEGINTS #define NSMALLPOSINTS _PY_NSMALLPOSINTS int _PyLong_Init(PyInterpreterState *interp) { for (Py_ssize_t i=0; i < NSMALLNEGINTS + NSMALLPOSINTS; i++) { sdigit ival = (sdigit)i - NSMALLNEGINTS; int size = (ival < 0) ? -1 : ((ival == 0) ? 0 : 1); PyLongObject *v = _PyLong_New(1); if (!v) { return -1; } Py_SET_SIZE(v, size); v->ob_digit[0] = (digit)abs(ival); interp->small_ints[i] = v; } interp->int_max_str_digits = _Py_global_config_int_max_str_digits; if (interp->int_max_str_digits == -1) { interp->int_max_str_digits = _PY_LONG_DEFAULT_MAX_STR_DIGITS; } return 0;
>>> a = 1 >>> b = 1 >>> c = 2 >>> id(a) 19112280 >>> id(b) 19112280 >>> id(c) 19112256 >>> id(a + 1) 19112256
大整数对象(python3中没找到源码,不清楚有没有实现,但在python2中有实现):我们不能保证只有[-5, 256]的数字才会被经常使用,对于大整数对象,python划分了一块区域,在运行时创建他们后,不使用便放入这一块内存池中,需要时再取出来,这样就省去了分配内存空间的时间,分配内存是一个耗时操作。
>>> for i in range(10000): ... pass ... >>> a = 1 >>> b = 1 >>> id(a) == id(b) True >>> a = 1000 >>> b = 1000 >>> id(a) == id(b) False >>> a = 9999 >>> b = 9999 >>> id(a) == id(b) False >>>
源码彩蛋
Python-3.10.10\Objects\longobject.c 第二行
/* XXX The functional organization of this file is terrible */
Comments | NOTHING