自从看了《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; } =>
睡觉了,明天再写。祝贺今天张聪学长升职。