首页 > 编程语言 > 详细

CPython-内置int类型

时间:2021-04-10 22:24:19      阅读:14      评论:0      收藏:0      [点我收藏+]

  内置int类型关联的数据结构包括:int数据对象、int类型对象。PyIntObject结构体用来表示int数据对象,PyInt_Type是int类型对象(PyTypeObject)。

  

  PyIntObject

  先看PyIntObject结构体的定义:

typedef struct {
    PyObject_HEAD
    long ob_ival;
} PyIntObject;

  其中PyObject_HEAD是PyObject的数据部分(占16字节),加上int数据对象的数值字段(ob_ival),共20字节(正如上一节所见)。

  由此可见int数据对象是定长对象(固定长度)。

 

  PyInt_Type

  再看PyInt_Type的定义:

PyTypeObject PyInt_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)    // ob_type == &PyType_Type
    "int",
    sizeof(PyIntObject),
    0,
    (destructor)int_dealloc,                    /* tp_dealloc */
    (printfunc)int_print,                       /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    (cmpfunc)int_compare,                       /* tp_compare */
    (reprfunc)int_to_decimal_string,            /* tp_repr */
    &int_as_number,                             /* tp_as_number */
    ...
   int_new,                                    /* tp_new */
};

  其中"PyVarObject_HEAD_INIT(&PyType_Type, 0)",将PyInt_Type对象中的ob_type字段设置为PyType_Type对象的地址(即指向PyType_Type对象)。

  int数据对象包括数值运算,数值运算操作的定义在int_as_number部分。

 

  PyIntObject创建

[Python]
>>> x=200
>>> y=int(‘2000‘)
>>> type(y)
<type int>

  "x=200"会创建一个int数据对象,会直接用整数来创建PyIntObject对象(PyInt_FromLong)。

  "y=int(‘2000‘)"也会创建一个int数据对象,但它是通过PyInt_Type中定义的tp_new函数(int_new)来显式创建,在int_new中会根据传递参数的类型,分别通过PyNumber_Int(浮点数)、PyInt_FromLong(整数)、PyInt_FromString(字符串)、PyInt_FromUnicode(Unicode字符串)函数,对参数进行处理最终会调用PyInt_FromLong来创建PyIntObject对象。

  所以就直接来看PyInt_FromLong函数的定义:

PyObject * PyInt_FromLong(long ival)
{
    register PyIntObject *v;
   // (1)小整数
#if NSMALLNEGINTS + NSMALLPOSINTS > 0 if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) { v = small_ints[ival + NSMALLNEGINTS]; Py_INCREF(v); return (PyObject *) v; } #endif
   // (2)通用整数对象空闲列表 if (free_list == NULL) { if ((free_list = fill_free_list()) == NULL) return NULL; } // (3)取用和初始化整数对象 v = free_list; free_list = (PyIntObject *)Py_TYPE(v); (void)PyObject_INIT(v, &PyInt_Type); v->ob_ival = ival; return (PyObject *) v; }

  (1)小整数

    NSMALLNEGINTS宏表示(负)小整数数目,NSMALLPOSINTS宏表示(正)小整数数目。

    如果开启了小整数对象缓存,并且ival在小整数的范围内(-5 ~ 256),直接从小整数对象数组(samll_ints)中返回PyIntObject对象。

      #define NSMALLPOSINTS    257
      #define NSMALLNEGINTS    5
      static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];

    因为小整数被使用的频率非常高,特别是循环中和临时变量,采用小整数对象数组可以达到复用的目的。小整数数组的长度或许可以根据需要进行调整,即改变NSMALLNEGINTS和NSMALLPOSINTS宏定义。

  (2)通用整数对象空闲列表

    除小整数之外的通用整数对象,会从空闲列表中返回,如果空闲列表为空(NULL)则需要先填充它。

  (3)取用和初始化整数对象

    取用空闲列表首对象,并将空闲列表指针后移(ob_type字段用作链表指针来指向下一个空闲对象)。对于空闲列表的维护后面做进一步讲解。

    被取用的整数对象,需要给ob_ival字段赋值,同时将ob_type指向类型对象PyInt_Type。可见ob_type字段在不同情况下用作不同用途。

 

  下面演示小整数对象和通用整数对象的区别:

[Python]
>>> a=257 >>> b=257 >>> id(a) 17697476 >>> id(b) 17697436 # 同数值通用整数对象地址不同 >>> c=256 >>> d=256 >>> id(c) 22820468 >>> id(d) 22820468 # 同数值小整数对象地址相同

 

  填充空闲列表的功能在fill_free_list函数中定义:

static PyIntObject * fill_free_list(void)
{
    PyIntObject *p, *q;
    // (1)通用整数对象块创建
    p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));
    if (p == NULL)
        return (PyIntObject *) PyErr_NoMemory();
    ((PyIntBlock *)p)->next = block_list;
    block_list = (PyIntBlock *)p;
    // (2)块内整数对象串联
    p = &((PyIntBlock *)p)->objects[0];
    q = p + N_INTOBJECTS;
    while (--q > p)
        Py_TYPE(q) = (struct _typeobject *)(q-1);
    Py_TYPE(q) = NULL;
    return p + N_INTOBJECTS - 1;
}

  (1)通用整数对象块创建

    每次填充空闲列表是以块为单位进行的,PyIntBlock中定义了块指针和PyIntObject对象数组(1K内存以内的数组),具体见代码。

#define BLOCK_SIZE      1000    /* 1K less typical malloc overhead */
#define BHEAD_SIZE      8       /* Enough for a 64-bit pointer */
#define N_INTOBJECTS    ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject))

struct _intblock {
    struct _intblock *next;
    PyIntObject objects[N_INTOBJECTS];
};

typedef struct _intblock PyIntBlock;

  (2)块内整数对象串联

    将PyIntBlock内整数对象数组中的各个对象,从后往前将整数对象串联起来(ob_type字段),方便取用和回收。将PyIntBlock中最后的PyIntObject对象指针返回。

 

  PyIntObject销毁

    PyInt_Type对象中定义了对象销毁函数(int_dealloc),看函数定义:

static void int_dealloc(PyIntObject *v)
{
    if (PyInt_CheckExact(v)) {
        // (1)插入空闲列表
        Py_TYPE(v) = (struct _typeobject *)free_list;
        free_list = v;
    }
    else
        Py_TYPE(v)->tp_free((PyObject *)v);
}

  (1)插入空闲列表

    PyIntObject对象销毁的时候不是直接释放内存,而是将其插入空闲列表(free_list),这样就和创建过程能够联系起来。

    另外的分支表示作为整数的派生类对象,通过PyTypeObject对象中的tp_free操作完成销毁。

  小整数对象池(small_ints)和通用整数对象池(free_list)的使用能够达到PyIntObject对象复用的目的,减少PyIntObject对象创建,但会增加部分内存占用。

 

  下面演示PyIntObject对象创建和销毁过程中free_list的变化。

技术分享图片

 

  PyIntObject加法运算

    整数运算操作在int_as_number部分定义,可以查看其源码:

static PyNumberMethods int_as_number = {
    (binaryfunc)int_add,        /*nb_add*/
    (binaryfunc)int_sub,        /*nb_subtract*/
    (binaryfunc)int_mul,        /*nb_multiply*/
    (binaryfunc)int_classic_div, /*nb_divide*/
    (binaryfunc)int_mod,        /*nb_remainder*/
    (binaryfunc)int_divmod,     /*nb_divmod*/
    (ternaryfunc)int_pow,       /*nb_power*/
    (unaryfunc)int_neg,         /*nb_negative*/
    (unaryfunc)int_int,         /*nb_positive*/
    (unaryfunc)int_abs,         /*nb_absolute*/
    (inquiry)int_nonzero,       /*nb_nonzero*/
    (unaryfunc)int_invert,      /*nb_invert*/
    (binaryfunc)int_lshift,     /*nb_lshift*/
    (binaryfunc)int_rshift,     /*nb_rshift*/
    (binaryfunc)int_and,        /*nb_and*/
    (binaryfunc)int_xor,        /*nb_xor*/
    (binaryfunc)int_or,         /*nb_or*/
    int_coerce,                 /*nb_coerce*/
    (unaryfunc)int_int,         /*nb_int*/
    (unaryfunc)int_long,        /*nb_long*/
    (unaryfunc)int_float,       /*nb_float*/
    (unaryfunc)int_oct,         /*nb_oct*/
    (unaryfunc)int_hex,         /*nb_hex*/
    0,                          /*nb_inplace_add*/
    0,                          /*nb_inplace_subtract*/
    0,                          /*nb_inplace_multiply*/
    0,                          /*nb_inplace_divide*/
    0,                          /*nb_inplace_remainder*/
    0,                          /*nb_inplace_power*/
    0,                          /*nb_inplace_lshift*/
    0,                          /*nb_inplace_rshift*/
    0,                          /*nb_inplace_and*/
    0,                          /*nb_inplace_xor*/
    0,                          /*nb_inplace_or*/
    (binaryfunc)int_div,        /* nb_floor_divide */
    (binaryfunc)int_true_divide, /* nb_true_divide */
    0,                          /* nb_inplace_floor_divide */
    0,                          /* nb_inplace_true_divide */
    (unaryfunc)int_int,         /* nb_index */
};

  上面这些操作都是常用的数值运算,这里只针对加法操作进行分析,查看int_add函数源码:

static PyObject * int_add(PyIntObject *v, PyIntObject *w)
{
    register long a, b, x;
    // (1)数值运算
    CONVERT_TO_LONG(v, a);
    CONVERT_TO_LONG(w, b);
    x = (long)((unsigned long)a + b);
    if ((x^a) >= 0 || (x^b) >= 0)
        return PyInt_FromLong(x); // (2)创建新PyIntObject对象
    // (3)溢出时创建PyLongObject对象
    return PyLong_Type.tp_as_number->nb_add((PyObject *)v, (PyObject *)w);
}

  (1)数值运算

  (2)创建新PyIntObject对象

    如果加法运算的结果没有溢出,则会创建新的PyIntObject对象来存放结果并返回。

  (3)溢出时创建PyLongObject对象

 

  int数据对象是定长对象,同时它也是不可变对象(与直观印象相反)。对一个int变量的重新赋值,其实包括销毁原对象和创建新对象两步。以下是演示代码:

>>> a=258
>>> id(a)
17697476
>>> a=259
>>> id(a)
17697436

  

  定长不可变对象类型还有float、long。

 

CPython-内置int类型

原文:https://www.cnblogs.com/carlliu3/p/14641475.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!