logo
当前位置:首 页 > 编程技术 >后端开发 >python > 查看文章

Python 源码阅读:int

python, 后端开发, 编程技术 你是第2871个围观者 0条评论 供稿者:

代码我也仅仅是粗粗读了一遍, 可能出现疏漏和理解错误, 发现了望指出哈.

示例

>>> a = 1

>>> b = 1

>>> id(a) == id(b)

True

 

>>> c = 257

>>> d = 257

>>> id(c) == id(d)

False

 

#在python2.x中, 对于大的序列生成, 建议使用xrange(100000) 而不是range(100000), why?

源码位置 Include/intobject.h |

Objects/intobject.c


PyIntObject

typedef struct {

    PyObject_HEAD

    long ob_ival;

} PyIntObject;

结构


几个构造方法

# 从字符串, 生成PyIntObject对象

PyAPI_FUNC(PyObject *) PyInt_FromString(char*, char**, int);

 

# 从Py_UNICODE, 生成PyIntObject对象

#ifdef Py_USING_UNICODE

PyAPI_FUNC(PyObject *) PyInt_FromUnicode(Py_UNICODE*, Py_ssize_t, int);

#endif

 

# 从long值, 生成PyIntObject对象

PyAPI_FUNC(PyObject *) PyInt_FromLong(long);

 

PyAPI_FUNC(PyObject *) PyInt_FromSize_t(size_t);

PyAPI_FUNC(PyObject *) PyInt_FromSsize_t(Py_ssize_t);

这几个方法, 只需要关注

# 因为大家最后都调用这个方法完成对象生成

PyAPI_FUNC(PyObject *) PyInt_FromLong(long);


具体的构造方法 PyInt_FromLong

这个方法的定义

PyObject *

PyInt_FromLong(long ival)

{

    register PyIntObject *v;

 

    /* MARK: 如果, 值在小整数范围内, 直接从小整数对象池获取得到对象 */

 

    #if NSMALLNEGINTS + NSMALLPOSINTS > 0

    if (NSMALLNEGINTS  ival & ival  NSMALLPOSINTS) {

 

        /* MARK: small_ints是什么后面说 */

        v = small_ints[ival + NSMALLNEGINTS];

        // 引用+1

        Py_INCREF(v);

 

        /* 这里先忽略, 计数 */

        #ifdef COUNT_ALLOCS

            if (ival >= 0)

                quick_int_allocs++;

            else

                quick_neg_int_allocs++;

        #endif

 

        // 返回

        return (PyObject *) v;

    }

    #endif

 

    // 如果free_list还不存在, 或者满了

    if (free_list == NULL) {

        // 新建一块PyIntBlock, 并将空闲空间链表头部地址给free_list

        if ((free_list = fill_free_list()) == NULL)

            // 如果失败, 返回

            return NULL;

    }

 

    // 从free_list分出一个位置存放新的整数

 

    /* Inline PyObject_New */

    // 使用单向链表头位置

    v = free_list;

 

    // free_list指向单向链表下一个位置

    free_list = (PyIntObject *)Py_TYPE(v);

 

    // 初始化对象, 类型为PyInt_type, 值为ival

    PyObject_INIT(v, &PyInt_Type);

    v->ob_ival = ival;

 

    // 返回

    return (PyObject *) v;

}

注意这里的Py_TYPE()方法, 在我们第一篇文章里面有提到, 不知道的回去复习下对象的数据结构

#define Py_TYPE(ob)             (((PyObject*)(ob))->ob_type)

简而言之:

1. 先判断数值是否是小整数, 是的话从小整数对象池里面直接返回

(这个池固定大小, 下一点讲)

 

2. 如果不是, 从通用整数对象池里面取一个, 初始化返回

(如果这时候通用整数对象池还不存在或者已经满了, 新建一个池加入维护. 通用整数对象池后面讲)


小整数对象池

先看定义

#ifndef NSMALLPOSINTS

#define NSMALLPOSINTS           257

#endif

 

#ifndef NSMALLNEGINTS

#define NSMALLNEGINTS           5

#endif

 

#if NSMALLNEGINTS + NSMALLPOSINTS > 0

/* References to small integers are saved in this array

so that they can be shared.

The integers that are saved are those in the range

-NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).

*/

 

static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];

#endif

其实, 小整数对象池就是一个PyIntObject指针数组(注意是指针数组), 大小=257+5=262, 范围是[-5, 257) 注意左闭右开. 即这个数组包含了262个指向PyIntObject的指针.

结构

创建整数时, 如果在[-5, 257)范围, 直接返回已经存在的整数对象指针, 所以我们看到开头的例子, id比较一个true/一个false

小整数对象池, 在一开始就初始化了, 其初始化代码

int

_PyInt_Init(void)

{

    PyIntObject *v;

    int ival;

 

    // 注意这里, free_list再次出现

 

#if NSMALLNEGINTS + NSMALLPOSINTS > 0

 

    // 循环, 逐一生成

    for (ival = –NSMALLNEGINTS; ival ob_ival = ival;

 

        // 放到数组里

        small_ints[ival + NSMALLNEGINTS] = v;

    }

#endif

 

    return 1;

}

代码很眼熟吧, 觉得不眼熟回上面看代码

结论

1. 小整数对象池缓存 [5, 257) 内的整数对象, 数值在这个范围的整数对象有且只存在一个

 

2. 小整数对象池, 只是一个指针数组, 其真正对象依赖通用整数对象池

通用整数对象池1 – 基础结构PyIntBlock

首先, 有个数据结构PyIntBlock

#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;

回忆一下PyIntObject结构(1个int, 1指针, 1个long), size=4+4+4(先这么算), N_INTOBJECTS = 82

结构

通用整数对象池2 – 创建过程及运行时结构

有两个指针

# 指向一个block

static PyIntBlock *block_list = NULL;

 

# 指向一个PyIntObject

static PyIntObject *free_list = NULL;

生成过程的定义

// 初始化一个PyIntBlock

static PyIntObject *

fill_free_list(void)

{

    PyIntObject *p, *q;

    // 建立一个新的block

    /* Python’s object allocator isn’t appropriate for large blocks. */

    p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));

 

    // 建立失败(内存耗光了)

    if (p == NULL)

        return (PyIntObject *) PyErr_NoMemory();

 

    // block_list指向新的PyIntBlock节点

    ((PyIntBlock *)p)->next = block_list;

    block_list = (PyIntBlock *)p;

 

    /* Link the int objects together, from rear to front, then return

the address of the last int object in the block. */

 

    // p=block里面 PyIntObjects数组头地址, q是尾地址

    p = &((PyIntBlock *)p)->objects[0];

    q = p + N_INTOBJECTS;

 

    // 从尾部开始向首部移动, 利用对象里的ob_type指针(相当于使用这个字段, ob_type不作为原来的用途), 建立起一个单向链表

    // 这个单向链表的头部是数组的最后一个

    while (q > p)

        Py_TYPE(q) = (struct _typeobject *)(q1);

    Py_TYPE(q) = NULL; // 单向链表最后一个元素的next指向null

 

    // 返回单向链表的头地址!!!

    return p + N_INTOBJECTS – 1;

 

}

新建第一个时, 只有一个

从里面拿整数时, 取free_list指向的节点, 然后free_list指向链表下一个节点

当一个block用完了之后, 即free_list=NULL, 此时要新建另一个PyIntBlock

新建第二个

通用整数对象池3 – 删除一个整数时

定义

#define PyInt_CheckExact(op) ((op)->ob_type == &PyInt_Type)

 

static void

int_dealloc(PyIntObject *v)

{

    // 是整数类型, 将对象放入free_list单向链表头

    if (PyInt_CheckExact(v)) {

        Py_TYPE(v) = (struct _typeobject *)free_list;

        free_list = v;

    }

    else

        Py_TYPE(v)->tp_free((PyObject *)v); //不是整数类型, 对应类型析构

}

可以看到, 回收的时候, 把空间给放回到free_list了, 后面接着用

block_list维护着所有PyIntBlock列表, 查看源码注释可以看到

PyIntBlocks are never returned to the

system before shutdown (PyInt_Fini).

即, PyIntBlock申请的所有内存, 在Python结束之前, 都不会被释放

所以, 使用range(100000), 运行后, 虽然程序结束了, 但是整数占用空间还在.

 

建议对大范围的序列生成使用xrange

 

python3.x不用担心这个问题

说说梦想,谈谈感悟 ,聊聊技术,有啥要说的来github留言吧 https://github.com/cjx2328

—— 陈 建鑫

陈建鑫
footer logo
未经许可请勿自行使用、转载、修改、复制、发行、出售、发表或以其它方式利用本网站之内容。站长联系:cjx2328#126.com(修改#为@)
Copyright ©ziao Studio All Rights Reserved. E-mail:cjx2328#126.com(#号改成@) 沪ICP备14052271号-3