首页 > 代码库 > [python]源码-对象的创建和行为
[python]源码-对象的创建和行为
(明天论文就要送审了!!!距离毕业一个月!!!)
现在还记得刚开始学python时候被这种动态语言惊到的那种感觉,列表和字典对象可以随意伸缩,简直不能更帅了,但是一直不知道内部到底是怎么实现的,python源码用C实现的,但是C是过程性语言啊。
说怎么实现之前,先捋捋什么是对象,对这个我觉得《python源码剖析》这本书里面的解释很有意思:“一个对象实际上就是一片被分配的内存空间,这些内存可能是连续的,也可能是不连续的,这都不重要,重要的是这片内存在更高的层次上可以作为一个整体来考虑,这个整体就是对象,在这片内存中,存储着一系列的数据以及可以对这些数据进行修改和读取操作的代码”,一个很有意思的角度。
一个对象被创建后,它所占用的内存大小就是不可变的了,这意味着对可变长度的数据来说(比如字符串),只能在对象内部维护一个指向可变大小内存区域的指针。
在python中,一切皆是对象,其中所有对象的核心就是一个叫PyObject的结构体
1 typedef struct _object{ 2 int ob_refcnt; 3 struct _typeobject *ob_type; 4 }PyObject;
其中,ob_refcnt为引用计数,_typeobject决定了对象的类型,ob_type指向了一个类型对象(我的理解:基类),这个结构体将出现在所有对象所占有内存的最开始的地方,就比如在一个int类中:
1 typedef struct{ 2 PyObject _HEAD; 3 long ob_ival; 4 }PyIntObject;
int值得信息就保存在ob_ival中,这是python2.5中的int对象,如果是list这种可变长度的对象,这种方法显然是不行的,那么就把头部结构体PyObject中加一个表示此对象占有多少个对象的信息ob_size,这个值也就表明了边长对象中一共有多少个元素:
1 #define PyObject_VAR_HEAD 2 PyObject_HEAD 3 int ob_size; 4 5 typedef struct{ 6 PyObject_VAR_HEAD 7 }PyVarObject;
所以,python中对象的内存表示是下面这个样子的:
从上面可以看到,对象的类型完全由ob_type指向的类型对象决定的,那么类型对象是什么鬼?
typedef struct _typedefobject{ PyObject_VAR_HEAD char*tp_name; int tp_basicsize,tp_itemsize; destructor tp_dealloc; printfunc tp_print; hashfunc tp_hash; ternaryfunc tp_call; /*……*/ }PyTypeObject;
*tp_basicsize,tp_itemsize指定了分配内存空间的大小
*其余的带有func字眼的指向函数的指针表明了这个对象携带的操作
那么,对象是怎么创建的?
首先要说明在python新式类中,所有的对象的基类都是object,就像java中所有对象的基类都是Object所构成的单根类体系一样,也就是说,所有的对象都会自动继承其基类的所有方法。
在需要创建一个对象的时候,比如创建int对象需要用到的PyInt_Type对象时候,先寻找PyInt_Type类中的tp_new指针,如果这个指针为null,就寻找其父类,比如object中的tp_new指针,用这个指针指向的函数来创建对象,它指向的函数会首先查看PyInt_Type中的tp_basicsize字段,根据这个字段的信息来决定为这个对象分配多少内存,当创建完成后,会在PyInt_Type中寻找tp_init,用其指向的函数来初始化tp_new分配的内存。我理解的是,tp_new就相当于python类中的__new__静态类方法,tp_init就相当于__init__方法。
可以看到,在PyTypeObject中有很多只想函数的指针,这些函数指针也就决定了对象的行为,其中有三个非常重要的函数指针指向三个操作族:
tp_as_number-------->PyNumberMethods
tp_as_sequence------>PySequenceMethods (列表list)
tp_as_mapping------>PyMappingMethods (字典dict)
对每一种对象来说,可以同时指向三种操作族,可以通过哪些钩子方法实现,比如__getitem__,__setitem__
参考资料:python源码剖析
[python]源码-对象的创建和行为