在python中一切皆是对象,那么这种机制是如何实现的呢?下面就让我们从python的老巢“源码”来看看这个神秘的机制。
typedef struct _object {
PyObject_HEAD
} PyObject;
typedef struct {
PyObject_VAR_HEAD
} PyVarObject;
看到上面这两个结构体了吗?它们就是万源之源,所有python对象的祖先,为什么有两个祖先呢?这就要从现实时间的数据对象谈起,我们有些数据,像数字、固定矩阵这类数据其元素个数是固定不变的,而像字符串、集合、字典这类数据其元素个数是会上下浮动的。这样python的发明者在设计python时就为这两类数据创造了两个祖先(像人有男人与女人一样,好像不太确切,但本质就是有两个祖先了):定长数据类型与变长数据类型。
这两个结构体里面的两个变量,其实都是些宏。从源中我们可以找到这些宏的展开:
#define PyObject_HEAD
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
#define PyObject_VAR_HEAD
PyObject_HEAD
Py_ssize_t ob_size;
看得出来,类型多了的这个ob_size就说明了它的变长特性(元素的个数)。
下面我们来仔细看看PyObject_HEAD这个宏的内容:
_PyObject_HEAD_EXTRA:这个宏展开时要么是双向链表的前后指针要么是空,是在跟踪所有对象时使用:想像一下,一个双向链表把python所有的对象串联起来的场景,好宏大呀!
ob_refcnt:这个属性记录该对象被他人引用的次数(后面发现,这个东西是用来进行内存管理的:引用计数式的垃圾回收机制)
ob_type:这个玩意是个重头戏,它实现了python的多态(何谓多态:对于不同的类型的对象,相同名称的方法其表现行为不同),何以见得呢?下面就让我们重点分析下这个ob_type,首先看看ob_type的原型是什么:
typedef struct _typeobject {
PyObject_VAR_HEAD
const char *tp_name;
Py_ssize_t tp_basicsize, tp_itemsize;
printfunc tp_print;
。。。
PyNumberMethods *tp_as_number;
PySequenceMethods *tp_as_sequence;
PyMappingMethods *tp_as_mapping;
。。。
} PyTypeObject;
这个结构体在源代码中整整占了84行,天呢!它是个什么,这么多内容。我们重点看几个字段:
PyObject_VAR_HEAD
:嗯,这不是变长数据类型它老祖先里的头‘骨’吗?怎么这里还有呢?没错,你猜对了,我们指明某个对象类型的对象原型 PyTypeObject也是一个对象。至于为什么是变长类型的对象祖先的头’骨‘,还没搞清楚^0^。
tp_print:多态的内部原理开始显露了吧!就是通过这个type对象来为具体不同的对象提供不同的方法实现,尽量方法名字都一样。
PyNumberMethods,PySequenceMethods,PyMappingMethods:其实和上面这个tp_print一样,只不它们是个函数族(由函数集构成),类数字操作(比如支持+-×/)、类序列操作(比如[n:m]切片操作)、字典操作(比如dict[key]操作)。
下面就int类型对象对上面这些原理进行验证:
typedef struct {
PyObject_HEAD
long ob_ival;
} PyIntObject;
这就是我们的int类型对象,其头部呢?在python源中可以看出,用一系列宏来实现头部的初始化:
#define PyObject_HEAD_INIT(type)
_PyObject_EXTRA_INIT
1, type,
这里的type,从源码中看出,有一个PyType_Type类型,它就是那个描述类型的类型的对象。有点绕了,但是仔细理解下来,也没什么,无外乎:一个int数据,有一个专门描述int类型数据的类型对象,但是不光有int类型的类型对象还有string类型的类型对象。那么就需要有一个要对象来描述这些类型对象,这就是PyType_Type,也就是它可以测试一个对象是不是类型对象。