Python整数系统

自从看了《Python源码剖析》,越来越觉得这是一本操作系统入门书籍。毕竟虚拟机也带有点操作系统的味道,今天逛google,发现以前的一个好朋友也在用Python。以前什么事情都是被她超越,这次不能输给她了。随着对Python的深入,我现在开始明白数据结构的重要性了。只有明白了数据结构和一些常用算法,才能有机会对操作系统绝对清晰。熟悉汇编也是清晰操作系统的必经之路(因为汇编涉及了x86架构)。好了,废话不多说,进入正题。

上面这段话是我昨天写的,今天我看了Python源码的整数部分。先不说Python中整数系统,我先说说看这一章的感受。感受最大的就是C语言,大一的时候学了一点C语言的语法,之后就没有写C了。现在看C代码来真心相当吃力,之前还一直没有体会,这可能跟我那糟糕的数据结构有关,现在越来越明白数据结构的重要性,越来越明白C语言和数据结构是密不可分的东西,它与一些高级语言不一样,高级语言一般都把内存操作封装了,让你可以把注意力集中在业务逻辑上面。不懂C和数据结构的人永远没法绝对清晰。

在Python中整数是通过PyIntObject对象来实现的,PyIntObject是一个immutable(不可变对象),所谓不可变对象就是说PyIntObject所维护的值是永远不会变化的,也正是由于这些不变性,才实现了Python整数池机制。

typedef struct {
    PyObject_HEAD
    long ob_ival;
} PyIntObject;

Python中整数对象只不过是对C中long类型的一个包装(加上了类型信息和引用计数)。对象的元信息都是存放在类型对象上的,而PyIntObject的类型是PyInt_Type

PyTypeObject PyInt_Type = {
    PyObject_HEAD_INIT(&PyType_Type)
    0,
    "int",                          /* 名字 */
    sizeof(PyIntObject),            /* 创建时候内存大小 */
    0,
    (destructor)int_dealloc,        /* 析钩函数 */
    (printfunc)int_print,           /* print方法 */
    0,                              /* tp_getattr 跟tp_getattro类似,不过已经呗弃用 */
    0,                              /* tp_setter 跟tp_setattro类似,不过已经被弃用 */
    (cmpfunc)int_compare,           /* 比较操作 */
    (reprfunc)int_repr,             /* tp_repr repr()调用的方法*/
    &int_as_number,                 /* tp_as_number 数值类型操作的集合 */
    0,                              /* tp_as_sequence  序列类型操作的集合 */
    0,                              /* tp_as_mapping map类型操作的集合 */
    (hashfunc)int_hash,             /* tp_hash  计算哈希值的方法 */
    0,                              /* tp_call 这个我表示不可理解,int明明十一个可调用对象为什么这里会是0 */
    (reprfunc)int_repr,             /* tp_str str()调用的方法 */
    PyObject_GenericGetAttr,        /* tp_getattro */
    0,                              /* tp_setattro */
    0,                              /* tp_as_buffer 不知道是干嘛用的 */
    PyTPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | PyTPFLAGS——BASETYPE     /* tp_flags */
    int_doc,                        /* tp_doc   __doc__ */
    0,                              /* tp_traverse 不知道是干嘛用的*/
    0,                              /* tp_clear 不知道是干嘛用的*/
    0,                              /* tp_richcompare 富比较 */
    0,                              /* tp_weaklistoffset */
    0,                              /* tp_iter 创建迭代器 */
    0,                              /* tp_iternext 迭代器的next()方法 */
    int_methods,                    /* tp_methods 成员函数集合 */
    0,                              /* tp_members */
    0,                              /* tp_getset */
    0,                              /* tp_base */
    0,                              /* tp_dict */
    0,                              /* tp_descr_get 一个descript */
    0,                              /* tp_descr_set 一个descript */
    0,                              /* tp_dictoffset */
    0,                              /* tp_init  初始化方法 */
    0,                              /* tp_alloc 申请内存 */
    int_new,                        /* tp_new 申请内存 */
    (freefunc)int_free,             /* tp_free 释放内存 */
}

static PyNumberMethods int_as_number = {
    (binaryfunc)int_add,    /* nb_add */
    (binaryfunc)int_sub,    /* nb_subtract */
    ...
    (binaryfunc)int_div,    /* nb_floor_divide */
    int_true_divide,        /* nb_true_divide */
    0,                      /* nb_inplace_floor_divide floor_divide*/
    0,                      /* nb_inplace_true_divide 真除*/
}
[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;   \
    }
好吧,其实我还看到了int类型的__doc__,这个坑爹的东西昨天坑我到一点多。 Python提供了3种途径用来创建PyIntObject对象
PyObject* PyInt_FromeLong(long ival)
PyObject* PyInt_FromString(char *s, char **pend, int base)
#ifdef Py_USING_UNICODE
    PyObject* PyInt_FromUnicode(Py_UNICODE *s, int length, int base)
#endif

今天还看到一句很牛逼的话:在Python中,所有的对象都是存活在系统堆上的。对于什么是系统堆我现在还没概念,所以操作系统真的很重要。不然永远不可能绝对清晰。

由于整数对象十分常用,如果频繁得申请释放内存会导致Python性能低下,所以这里采用了整数池的方式来提高性能(我个人觉得这个设计很牛逼,有些事情一个人做不了,但是如果一群人能做得更好)。整数池又分为小整数池和大整数池,这两者的差别在于一个是全部缓存(小整数),一个是部分缓存(大整数)。

小整数池在Python虚拟机激活的时候就会创建,那个时候申请一块内存,将常用的小整数(默认5-257)放在里面,需要用的时候去里面拿,永远也不会销毁。

大整数池是需要用到小整数之外的整数时,去操作系统申请一块内存,用来存放创建的整数,并缓存在这里,下次用的时候来这里拿。如果内存不够了,继续向操作系统申请。这些内存抽象成一个单向链表,由block_list维护,其中的所有空闲内存由free_list维护(也是一条单向链表)。

#define BLOCK_SIZE  1000
#define BHEAD_SIZE  8
#define N_INTOBJECTS    ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject))

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

typedef struct _intblock PyIntBlock;

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

创建和删除整数

PyObject* PyInt_FromLong(long ival)
{
    register PyIntObject *v;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
    if (-NSMALLNEGINTS <= ival="" &&="" <="" nsmallposints)="" {="" v="small_ints[ival" +="" nsmallnegints];="" py_incref(v);="" return="" (pyobject="" *)="" v;="" }="" #endif="" [2]:为通用整数对象池申请新的空间="" if="" (free_list="=" null)="" ((free_list="fill_free_list())" =="NULL)" null;="" [3]:(inline)内嵌pyobject_new的行为="" free_list="(PyIntObject" *)v="" -=""> ob_type;
    PyObject_INIT(v, &PyInt_Type);
    v -> ob_ival = ival;
    return (PyObject *)v;
}

睡觉了,明天再写。祝贺今天张聪学长升职。