美文网首页
Python源码学习笔记 2 整数对象

Python源码学习笔记 2 整数对象

作者: openex | 来源:发表于2018-04-04 16:51 被阅读0次

    Python中的整数类型是不可变对象,为了提高python运行效率,内部实现了小整数对象池(数组实现),和通用整数缓冲池(单链表实现)。小整数是可以复用的,而通用整数是即使数值相同也会创建两个不同的整数对象。

    1.PyIntObject

    该结构仅适用Python2版本,该版本下数字长度大于long型时,对象类型会转变为PyLongObject

    PyIntObject结构:

    [intobject.h]
    typedef struct {
        PyObject_HEAD //标准PyObject头
        long ob_ival; //实际存储数值得变量
    } PyIntObject;
    

    整数对象的创建:
    提供了三种创建方式,分类为long型创建或从字符串创建,但从字符串创建最终还是需要调用long型创建方式

     PyObject *PyInt_FromLong(long ival) //从long整数创建
     PyObject* PyInt_FromString(char *s, char **pend, int base) //从ASCII字符串创建
    #ifdef Py_USING_UNICODE 
      PyObject*PyInt_FromUnicode(Py_UNICODE *s, int length, int base) //从Unicode字符串创建
    #endif
    

    整数对象相减:

    [intobject.h]
    #define PyInt_AS_LONG(op) (((PyIntObject *)(op))->ob_ival)
    //宏,牺牲类型安全,换取执行效率
    
    [intobject.c]
    #define CONVERT_TO_LONG(obj, lng)       \
        if (PyInt_Check(obj)) {         \
            lng = PyInt_AS_LONG(obj);   \
        }                   \
        else {                  \
            Py_INCREF(Py_NotImplemented);   \
            return Py_NotImplemented;   \
        }
      
    static PyObject *
    int_sub(PyIntObject *v, PyIntObject *w)
    {
        register long a, b, x;
        CONVERT_TO_LONG(v, a);
        CONVERT_TO_LONG(w, b);
        x = a - b;
        if ((x^a) >= 0 || (x^~b) >= 0) //溢出检查
            return PyInt_FromLong(x);//此处印证了Python中整数对象是一个不可变对象
        return PyLong_Type.tp_as_number->nb_subtract((PyObject *)v,
                                 (PyObject *)w); //若溢出会将返回一个PyLongObject
    }
    

    2.小整数对象

    在编程中小整数对象使用频率很高(如循环标记),而从上文中我们已经得知Python中整数对象是不可变对对象,若没有特殊的方法处理,会导致频繁创建小整数对象,严重影响运行效率和浪费内存。
    在python内部通过创建一个PyIntObject *的数组small_ints来优化这个情况, 数组默认范围为[-5,257)

    [intobject.c]
    #ifndef NSMALLPOSINTS
         #define NSMALLPOSINTS       257 //正数
    #endif
    #ifndef NSMALLNEGINTS
         #define NSMALLNEGINTS       5 //负数
    #endif
    #if NSMALLNEGINTS + NSMALLPOSINTS > 0
         static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
    #endif
    
    

    3.通用整数对象

    对于小整数以外的其他整数,python内部使用多个“预先”分配的内存块来缓存这些整数对象,每一个内存块称为一个block_list, 未使用的空间使用free_list穿起来

    [intobject.c]
    
    #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]; //PyIntObject数组,用以存储整数
    };
    
    typedef struct _intblock PyIntBlock;
    
    static PyIntBlock *block_list = NULL;
    static PyIntObject *free_list = NULL;
    

    4.整数对象的创建与删除

    整数对象的创建

     [intobject.c]
    PyObject* PyInt_FromLong(long ival)
    {
         register PyIntObject *v; 
    #if NSMALLNEGINTS + NSMALLPOSINTS > 0  
    //判断小整数对象池是否激活
         if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) { //尝试使用小整数对象池
            v = small_ints[ival + NSMALLNEGINTS];
            Py_INCREF(v);
            return (PyObject *) v;
        }
    #endif
        if (free_list == NULL) {//当前block_list无空闲,需要申请新的PyIntBlock
            if ((free_list = fill_free_list()) == NULL) 
                return NULL;
        }
        //使用通用整数对象池
        v = free_list;
        free_list = (PyIntObject *)v->ob_type;
        PyObject_INIT(v, &PyInt_Type);
        v->ob_ival = ival;
        return (PyObject *) v;
    }
    

    整数对象的删除:
    对整数对象进行删除时,实际上是将该对象加入到free_list链表中,以便整型对象的缓冲,避免频繁的malloc操作

    static void
    int_dealloc(PyIntObject *v)
    {
        if (PyInt_CheckExact(v)) {
            /*将该整型对象加入到free_list链表中*/
            Py_TYPE(v) = (struct _typeobject *)free_list;
            free_list = v;
        }
        else
            Py_TYPE(v)->tp_free((PyObject *)v);//若为整型派生对象则仅调用指定的tp_free()
    }
    

    小整数对象池的初始化:
    在Python虚拟机初始化时_PyInt_Init会被调用,创建小整数缓冲池

    int
    _PyInt_Init(void)
    {
        PyIntObject *v;
        int ival;
    #if NSMALLNEGINTS + NSMALLPOSINTS > 0
        /* 小整数对象仍在block_list中创建,只是将创建后的对象地址存入对应位置small_ints中 */
        for (ival = -NSMALLNEGINTS; ival < NSMALLPOSINTS; ival++) {
            if (!free_list && (free_list = fill_free_list()) == NULL)
                return 0;
            v = free_list;
            free_list = (PyIntObject *)Py_TYPE(v);
            (void)PyObject_INIT(v, &PyInt_Type);
            v->ob_ival = ival;
            small_ints[ival + NSMALLNEGINTS] = v;//指针存入small_ints
        }
    #endif
        return 1;
    }
    

    fill_free_list实现:
    当free_list为NULL时创建新的整数对象,会触发fill_free_list操作,将malloc一个PyIntBlock,并将内部的object数组中每个整数对象,通过ob_type作为后继(牺牲掉类型安全)形成单链表关系

    [object.h]
    #define Py_TYPE(ob)             (((PyObject*)(ob))->ob_type)
    
    [intobject.c]
    static PyIntObject *
    fill_free_list(void)
    {
        PyIntObject *p, *q;
        /* Python's object allocator isn't appropriate for large blocks. */
        p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));
        if (p == NULL)
            return (PyIntObject *) PyErr_NoMemory();
        ((PyIntBlock *)p)->next = block_list; //将新block的next链接到旧block_list上
        block_list = (PyIntBlock *)p; //另新block为block_list头结点
        p = &((PyIntBlock *)p)->objects[0];
        q = p + N_INTOBJECTS;
        while (--q > p)
            Py_TYPE(q) = (struct _typeobject *)(q-1); //创建block内部空闲整数对象间连接关系
        Py_TYPE(q) = NULL;
        return p + N_INTOBJECTS - 1;
    }
    

    相关文章

      网友评论

          本文标题:Python源码学习笔记 2 整数对象

          本文链接:https://www.haomeiwen.com/subject/smxlhftx.html