Python源码剖析----第二章

第二章  Python的整数对象

2.1 基本概念
在Python中,整数对象是不可变对象,即创建一个PyIntObject对象之后,就再也不能改变该对象所维护的那个真实的整数值了。但在实际Python的应用程序中,整数的使用太过广泛,为避免频繁创建,Python为整数对象使用了一个巧妙的缓冲池机制。事实上,几乎python中所有的内建对象,都会有自己特有的对象池机制。

[intobject.h]
typedef struct{
 PyObject_HEAD #对象头结构
 long ob_ival; #整数对象的值
} PyIntObject;
从定义中看出,Python中的整数对象PyIntObject实际上是对C中原生类型long的一个简单包装,而与整数对象PyIntObject相关的元信息实际上都保存在与其对应的类型对象PyInt_Type中:
[intobject.c]
PyTypeObject PyInt_Type= {
 PyObject_HEAD_INIT(&PyType_Type)
 0,
 "int",
 sizeof(PyIntObject),
 0,
 (destructor)int_dealloc, /*tp_dealloc, 整数对象的析构操作*/
 (printfunc)int_print, /*tp_print, 打印PyIntObject对象 */
 0,
 0,
 (cmpfunc)int_compare, /* tp_compare, 比较操作*/
 (reprfunc)int_repr,
 &int_as_number, /*tp_as_number, 数值操作集合, PyNumberMethods中包含39个函数指针对应39中可选操作 */
 0,
 0,
 (hashfunc)int_hash,/*tp_hash, 获得Hash值*/
 0,
 (reprfunc)int_repr,/*tp_str,转化成PyStringObject对象*/
 PyObject_GenericGetAttr,
 0,
 0,
 int_doc,
 0,
 0,
 0,
 0,
 0,
 0,
 int_methods, /*tp_methods, 成员函数集合 */
 0,
 0,
 0,
 0,
 0,
 0,
 0,
 0,
 0,
 int_new,
 (freefunc)int_free,/*tp_free, PyIntObject对象的释放操作*/
);

2.2 Python中整数对象的系统结构

在实际的编程中,小数值的整数会被非常频繁地使用,如果在系统堆上频繁申请并释放空间,不仅降低运行效率,而且会造成大量内存碎片,严重影响Python的整体性能,因此Python中,对小整数对象使用了对象池计数,通过宏 [NSMALLEGINTS, NSMALLPOSINTS) 来确定小整数对象的定义区间,这些整数由Python直接将其对应的整数对象缓存在内存中,并将指针存放在small_ints中。这个对象池的创建及初始化的过程在Python初始化的阶段。

而对于大整数,由于空间限制,Python不可能将其完全缓存,而是以PyIntBlock结构为基础,通过两个单向列表(PyIntBlock block_list, PyIntObject *free_list)来维护一块内存空间由这些大整数轮流使用。

typedef struct _intblock{
	struct _intblock *next;
	PyIntObject objects[N_INTOBJECTS];
}PyIntBlock;
static PyIntBlock *block_list = NULL;
Static PyIntObject *free_list = NULL;
通过PyInt_FromLong可以考察到Python中一个整数对象PyIntObject的创建

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) {
        if ((free_list = fill_free_list()) == NULL)
            return NULL;
    }
    /* Inline PyObject_New */
    v = free_list;
    free_list = (PyIntObject *)Py_TYPE(v);
    PyObject_INIT(v, &PyInt_Type);
    v->ob_ival = ival;
    return (PyObject *) v;
}
PyInt_FromLong会首先判断小整数机制是否被激活,如果确认小整数对象池被激活且传入的值属于小整数范围,则直接返回在池中对应的对象,否则,则转向由block_list维护的通用对象池,从block中寻找一块可用于存储新的PyIntObject对象的内存, 如当前所有PyIntBlock中已经没有空闲内存块(freelist为空)时, Python将重新申请新的PyIntBlock并将其放入block_list, 而指针 *block_list会指向最新生成的block, 当Python需要销毁某block中的一个整数对象时,会将该对象的使用计数置零,但其所占用的内存并不会被归还给系统,而是继续有Python保留,并将其链入到free_list所维护的自由内存链表。

需要注意的是,通用对象池的大小并不是固定的,当一个整数对象的引用计数变为0时, 就会被Python回收,但是在int_dealloc中,仅仅是将该整数对象的内存重新加入到自由内存链表中,而不会将内存交回到系统堆。因此一旦系统堆中的某块内存被Python申请用于整数对象,那么这块内存在Python结束前,永远不会被释放。因此从Python中对通用整数对象池的共享机制可以看出,Python用于实现对象池的内存与历史上创建的整数对象的个数无关,而是与同一时刻共存的整数对象个数的最大值有关。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值