您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 信息化管理 > Python源码剖析
中清龙图教育,全球游戏50强教育品牌源码剖析——对象机制1.对象在Python的世界中,一切都是对象,一个整数是一个对象,一个字符串也是一个对象,更为奇妙的是,类型也是一个对象,整数类型是一个对象,字符串类型也是一个对象。从1980年Guido在那个圣诞节揭开Python世界的大幕开始,一直到现在,Python经历了一次一次的升级,但是其实现语言一直都是ANSIC。我们知道,C并不是一个面向对象的语言,那么在Python中,它的对象机制是如何实现的呢?对于人的思维来说,对象是一个比较形象的概念,而对于计算机来说,对象实际上是一个抽象的概念。计算机并不能理解这是一个整数,那是一个字符串,对于计算机来说,它所知道的一切都是字节。通常的说法是,对象是数据以及基于这些数据的操作的集合。在计算机上,一个对象实际上就是一片被分配的内存空间,这些内存可能是连续的,也有可能是离散的,这都不重要,重要的是这片内存在更高的层次上可以作为一个整体来考虑,这个整体就是一个对象。在这片内存中,存储着一系列的数据以及可以对这些数据进行修改或读取的一系列操作的代码。在Python中,对象就是在堆上申请的结构体,对象不能是被静态初始化的,并且也不能是在栈空间上生存的。唯一的例外就是类型对象(typeobject),Python中所有的类型对象都是被静态初始化的。在Python中,一个对象一旦被创建,它在内存中的大小就是不变的了。这就意味着那些需要容纳可变长度数据的对象只能在对象内维护一个指向一个可变大小的内存区域的指针。为什么要设定这样一条特殊的规则呢,因为遵循这样的规则可以使通过指针维护对象的工作变得非常的简单。因为一旦允许对象的大小可在运行期改变,我们可以考虑如下的情形。在内存中有对象A,并且其后紧跟着对象B。如果运行期某个时刻,A的大小增大了,这意味着必须将整个A移动到内存中的其他位置,否则A增大的部分将覆盖原本属于B的数据。一旦将A移动到内存中的其他位置,那么所有指向A的指针必须立即得到更新,光是想一想,就知道这样的工作是多么的恐怖。在Python中,所有的东西都是对象,而所有的对象都拥有一些相同的内容,这些内容在PyObject中定义,PyObject是整个Python对象机制的核心。中清龙图教育,全球游戏50强教育品牌页[object.h]typedefstruct_object{PyObject_HEAD}PyObject;实际上,PyObject是Python中不包含可变长度数据的对象的基石,而对于包含可变长度数据的对象,它的基石是PyVarObject:[object.h]typedefstruct{PyObject_VAR_HEAD}PyVarObject;这两个结构体构成了Python对象机制的核心基石,从代码中我们可以看到,Python的对象的秘密都隐藏在PyObject_HEAD与PyObject_VAR_HEAD中。[object.h]#ifdefPy_TRACE_REFS/*Definepointerstosupportadoubly-linkedlistofallliveheapobjects.*/#define_PyObject_HEAD_EXTRA\struct_object*_ob_next;\struct_object*_ob_prev;#define_PyObject_EXTRA_INIT0,0,#else#define_PyObject_HEAD_EXTRA#define_PyObject_EXTRA_INIT#endif/*PyObject_HEADdefinestheinitialsegmentofeveryPyObject.*/#definePyObject_HEAD\_PyObject_HEAD_EXTRA\intob_refcnt;\中清龙图教育,全球游戏50强教育品牌*ob_type;#definePyObject_VAR_HEAD\PyObject_HEAD\intob_size;/*Numberofitemsinvariablepart*/在PyObject_HEAD中定义了每一个Python对象都必须有的内容,这些内容将出现在每一个Python对象所占有的内存的最开始的字节中,从PyObject_VAR_HEAD的定义可以看出,即使对于拥有可变大小数据的对象,其最开始的字节也含有相同的内容,这就是说,在Python中,每一个对象都拥有相同的对象头部。这就使得在Python中,对对象的引用变得非常的统一,我们只需要用一个PyObject*就可以引用任意的一个对象,而不论该对象实际是一个什么对象。在PyObject_HEAD的定义中,我们注意到有一个ob_refcnt的整形变量,这个变量的作用是实现引用计数机制。对于某一个对象A,当有一个新的PyObject*引用该对象时,A的引用计数应该增加;而当这个PyObject*被删除时,A的引用计数应该减少。当A的引用计数减少到0时,A就可以从堆上被删除,以释放出内存供别的对象使用。在PyObject_HEAD中,我们注意到ob_type是一个指向_typeobject结构体的指针,那么这个结构体是一个什么东西呢?实际上这个结构体也是一个对象,它是用来指定一个对象类型的类型对象。这个类型对象我们将在后边详细地考察。现在我们看到了,在Python中实际上对象机制的核心非常的简单,一个是引用计数,一个就是类型。而对于拥有可变长度数据的对象,这样的对象通常都是容器,我们可以在PyObject_VAR_HEAD中看到ob_size这个变量,这个变量实际上就是指明了该对象中一共包含了多少个元素。注意,ob_size指明的是元素的个数,而不是字节的数目。比如对于Python中最常用的list,它就是一个PyVarObject对象,如果某一时刻,这个list中有5个元素,那么PyVarObject.ob_size的值就是5。2.类型对象在上面的描述中,我们看到了Python中所有对象的对象头的定义。所以,当内存中存在某一个Python的对象时,该对象的开始的几个字节的含义一定会符合我们的预期。但是,当我们把眼光沿着时间轴上溯,就会发现一个问题。当在内存中分配空间,创建对象的时候,毫无疑问地,必须要知道申请多大的空间。显然,这不会是一个定值,因为对于不同的对象,需要不同的空间,一个整数对中清龙图教育,全球游戏50强教育品牌页象和一个字符串对象所需的空间肯定不同。那么,对象所需的内存空间的大小的信息到底在哪里呢?在对象头中显然没有这样的信息。实际上,内存空间大小这样的对象的元信息是与对象所属类型密切相关的,因此它一定会出现在与对象所对应的类型对象中。现在我们可以来详细考察一下类型对象_typeobject:[object.h]typedefstruct_typeobject{PyObject_VAR_HEADchar*tp_name;/*Forprinting,informatmodule.name*/inttp_basicsize,tp_itemsize;/*Forallocation*//*Methodstoimplementstandardoperations*/destructortp_dealloc;printfunctp_print;……/*Morestandardoperations(hereforbinarycompatibility)*/hashfunctp_hash;ternaryfunctp_call;……}PyTypeObject;在_typeobject的定义中包含了许多的信息,主要可以分为四类:1.类型名,tp_name,主要是Python内部以及调试的时候使用;2.创建该类型对象是分配内存空间的大小的信息,即tp_basicsize和tp_itemsize;3.与该类型对象相关联的操作信息,比如hashfunc,tp_hash就指明对于该类型的对象,如何生成其hash值。在Object.h中可以看到,hashfunc实际上是一个函数指针:typedeflong(*hashfunc)(PyObject*);在_typeobject中,包含了大量的函数指针,这些函数指针将用来指定某个类型的操作信息。这些操作主要分为标准操作(dealloc,print,compare),标准操作族(numbers,sequences,mappings),以及其他操作(hash,buffer,call…)。4.我们在下边将要描述的类型的类型信息。有趣的是我们在_typeobject的头部发现了PyObject_VAR_HEAD,这意味着类型实际上也是一个对象。我们知道在Python中,每一个对象都是对应一种类型的,那么一个有趣的问题就出现了,类型对象的类型是什么呢?这个问题听上中清龙图教育,全球游戏50强教育品牌页去很绕口,实际上确非常重要,对于其他的对象,可以通过与其关联的类型对象确定其类型,那么通过什么来确定一个对象是类型对象呢?答案就是PyType_Type:[typeobject.c]PyTypeObjectPyType_Type={PyObject_HEAD_INIT(&PyType_Type)0,/*ob_size*/type,/*tp_name*/sizeof(PyHeapTypeObject),/*tp_basicsize*/sizeof(PyMemberDef),/*tp_itemsize*/……PyObject_GC_Del,/*tp_free*/(inquiry)type_is_gc,/*tp_is_gc*/};前面提到,在Python中,每一个对象它的开始部分都是一样的。每一个对象都将自己的引用计数,类型信息保存在开始的部分中。为了方便对这部分内存的初始化,Python中提供了几个有用的宏:[object.h]#ifdefPy_TRACE_REFS#define_PyObject_EXTRA_INIT0,0,#else#define_PyObject_EXTRA_INIT#endif#definePyObject_HEAD_INIT(type)\_PyObject_EXTRA_INIT\1,type,中清龙图教育,全球游戏50强教育品牌的定义,初始化的动作就一目了然了。实际上,这些宏在类型对象的初始化中被大量地使用着。如果以一个整数对象为例,可以更清晰地看到一半的类型对象和这个特立独行的PyType_Type对象之间的关系:[intobject.c]PyTypeObjectPyInt_Type={PyObject_HEAD_INIT(&PyType_Type)0,int,sizeof(PyIntObject),……};现在我们可以放飞想象,看到一个整数对象在运行时的抽象的表示了,下图中的箭头表示ob_type:3.对象间的继承和多态通过PyObject和类型对象,Python利用C语言完成了C++所提供的继承和多态的特性。前面提到,在Python中所有的内建对象(PyIntObject等)和内部使用对象(PyCodeObject等)的最开始的内存区域都拥有一个PyObject。实际上,这一点可以视为PyIntObject,PyCodeObject等对象都是从PyObject继承而来。在Python创建一个对象,比如PyIntObject对象时,会分配内存,进行初始化。然后这个对象会由一个PyObject*变量来维护
本文标题:Python源码剖析
链接地址:https://www.777doc.com/doc-4234350 .html