Python PyLongObject 整数对象


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 */

声明:Hello World|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - Python PyLongObject 整数对象


我的朋友,理论是灰色的,而生活之树是常青的!