[Python源码剖析] Python的整数对象

Chilly_Rain posted @ 2013年7月30日 21:54 in Python源码剖析 , 2208 阅读

Python的对象由前面提到的知识可以分为定长对象和变长对象,同时从另一个角度上,Python的对象也可以分为可变对象和不可变对象。本章中的PyIntObject就是一个定长对象+不可变对象。

由于Python的程序中一般会对整数对象的使用极重,如果频繁地创造和销毁是极其影响效率的,因此Python的设计者给出了“整数对象池”这个概念,作为整数对象的缓冲池机制。言归正传,下面是PyIntObject的定义

[intobject.h]
typedef struct {
    PyObject_HEAD
    long ob_ival;
} PyIntObject;

而正如第一章所述,PyObject_HEAD中实际是一个类型对象指针和一个引用计数。PyIntObject对应的类型对象就是PyInt_Type,其就是PyTypeObject的一个实例。

这 个类型对象内容太多,我就不copy了,主要的内容包括名称,大小等基本属性以及一些函数集指针。函数集指针又包括了int_dealloc, int_free, int_repr,int_compare, int_as_number等。特别是最后一个,它定义了作为数值对象的所有可选操作,像int_add, int_sub等。

在Python的实现中,对于一些执行频繁的代码,都同时提供了函数和宏两个版本,比如PyInt_AS_LONG(宏), PyInt_AsLong(函数),调用者需要在安全和性能上做出选择。

创建一个PyIntObject对象可有三种途径,对应三种不同的输入参数类型,如下,但是实际上这里应用了Adpator Pattern的思想,后两个实际最终都只是通过在接口上做了转换后调用了PyInt_FromLong。

PyObject* PyInt_FromLong(long ival)
PyObject* PyInt_FromString(char* s, char** pend, int base)
PyObject* PyInt_FromUnicode(Py_UNICODE* s, int length, int base)

程序在运行期间,Python的整数对象并不是孤立存在地,而是形成了一个整数对象系统。先来看小整数对象。

由 于Python对象在运行期间都在存活在系统堆上的,因此如果没有优化机制,而是对这些频繁使用的小整数对象进行malloc和free,那么不仅降低运 行效率,也会造成内存碎片。因此小整数对象使用了对象池技术,同时由于整数对象是不可变对象,所以对象池中的对象都是被任意共享的。

那么多小的整数才算是小整数呢?默认范围是[-5, 257),当然也可以改,但是就得自己重新编译一遍源码了。

#define NSMALLPOSINTS 257
#define NSMALLNEGINTS 5
#if NSMALLPOSINTS + NSMALLNEGINTS > 0
    static PyIntObject* small_ints[NSMALLPOSINTS + NSMALLNEGINTS];
#endif

对于大整数对象了,Python运行环境提供一块内存,这些内存由大整数轮流使用,从而避免了不断malloc的工作。而 这块内存也是有自己的结构的,它是由一个称为block_list的指针维护的一个单向链表,其中每一个结点称为一个PyIntBlock,而每个 block中又维护着N_INTOBJECTS个PyIntObject对象,即objects数组。

struct _intblock {
    struct _intblock *next;
    PyIntObject objects[N_INOBJECTS];
};
typedef struct _intblock PyIntBlock;

static PyIntBlock* block_list = NULL;
static PyIntObject* free_list = NULL;

在Python运行的某个时刻,通常有一些block中的某些object正在被使用,而其它的则处于空闲状态,为了把这些空闲object组织起来,就有了free_list,它作为链表的表头,把所有空闲内存串成另一个单链表。

PyIntObject的创建过程中,上面的结构是如何起作用的呢?

首先,程序会检查是否有小整数对象池,如果有的话再检查要创建的这个PyIntObject是不是在小整数范围内,如果是的话,直接返回;如果上面的条件无法满足,那么就会求助于block_list和free_list,寻找一块可用的PyIntObject对象的内存。

if(free_list == NULL) {
    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;

当第一次调用PyInt_FromLong时,free_list是空(另外,当没有空闲block时它也是空),那么就会调用fill_free_list函数来创建新的内存。

// fill_free_list
PyIntObject *p, *q;
p = (PyIntObject*) PyMem_MALLOC(sizeof(PyIntBlock));
((PyIntBlock*)p)->next = block_list;
block_list = (PyIntBlock*) p;

p = &((PyIntBlock*)p)->objects[0];
q = p + N_INTOBJECTS;
while(--q > p)
    q->ob_type = (struct _typeobject*)(q-1);
q->ob_type = NULL;
return p + N_INTOBJECTS -1;

实际工作就是加一块block,放在block_list的头部,所以block_list总是会指向最新创建的PyIntBlock对象;然后对于其内部的所有PyIntObject,利用ob_type从后到前串起来, 最后返回最后一个object的位置作为free_list的赋值。

此时free_list只管理了一个block下的所有空闲 objects,block之间的objects如何关联呢?这发生在一个PyIntObject的引用计数减少到0的时候:在int_dealloc内,它会将自己的ob_type指针原free_list,再将free_list指向这个刚刚销毁的object。从这里也能看出,如果一块内存被申请 用作PyIntObject,那么它就永远不会归还给OS,而是一直保留着,因此PyIntObject占用的内存大小与同时共存的整数个数最大值有关。

如果要删除的对象是一个整数的派生类对象,那么int_dealloc也不会做任何动作,只是调用其类型对象中的tp_free。

最后要看的是小整数对象的创建时机:它们是在python初始化的时候,调用_PyInt_Init时被调用的。small_ints中保存的object 也是被block_list和free_list来管理的,只不过它们是永生不灭的,因此small_ints中总会贡献一次引用计数。

Avatar_small
celeb networth 说:
2023年6月05日 16:07

Finally find the info someone asked the other day about Taylor Swift on celeb networth post I was looking for it all over the web and luckily found it!

Avatar_small
12thmodelpaper2021. 说:
2023年7月01日 18:57

skilled writers who have teamed together to provide thorough news coverage of the most recent events in the nation (India). Our team is made up of professional writers and citizen journalists with a wide range of journalism interests who are committed about delivering education updates in the public interest while maintaining transparency.Our reporting team plans to 12thmodelpaper2021.in release the Education & Recruitment Update for all age groups and provide inside coverage to show the real picture of current occurrences.

Avatar_small
civaget 说:
2024年1月11日 23:12

I am very happy to read this. This is the kind of info that needs to be given and not the accidental misinformation that is at the other blogs. Appreciate your sharing this greatest doc. google doc dark mode

Avatar_small
seo service london 说:
2024年2月22日 15:25

our work is very good and I appreciate you and hopping for some more informative posts


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter