
在前一篇讲PyObject的时候,有讲到PyObject中有一个指针,是指向PyTypeObject的,现在我们就来研究一下PyTypeObject的组成。
PyTypeObject的定义十分长,代码我们一部分一部分贴。
typedef struct _typeobject {
/*看头部,发现_typeobject也是有PyObject头部*/
PyObject_VAR_HEAD
const char *tp_name; /* For printing, in format "<module>.<name>" */
Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */
/*
...
*/
} PyTypeObject;看第一部分,看到PyObject_VAR_HEAD。按照前一篇的说明,只要头部是PyObject_VAR_HEAD或者PyObject_HEAD的都可以看作PyObject的子类。但是这理解起来就有点费劲了。我们把PyObject_VAR_HEAD全部展开,得到。
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;/*_typeobject的定义里有_typeobject指针*/
Py_ssize_t ob_size;
看到了一个稍微有点费解的地方了。就是_typeobject里有_typeobject指针,这样会不会无限俄罗斯套娃呢?答案是当然不会了!这个案例在我们第一次学链表的时候就有了。想想是不是?
struct Node {
Node* next;
};在一个类型的声明里面包含这个类型的指针(注意,是指针),在C是允许的,毕竟指针就是一个地址变量。
tp_name在源代码中的注释就比较清楚了,就是用来打印的。
tp_basicsize和tp_itemsize是表明这个类型的一些空间大小的。至于设么是basicsize,什么是itemsize,我们之后在介绍其他对象的时候就会说到。
下一部分就是一些基础操作,看名字就很清晰大概是干嘛的了。比如tp_dealloc就是析构这个对象的时候会调用的函数。tp_print就是我们在python中print的时候会调用到的函数。
typedef struct _typeobject {
/*
...
*/
/* Methods to implement standard operations */
destructor tp_dealloc;
printfunc tp_print;
getattrfunc tp_getattr;
setattrfunc tp_setattr;
cmpfunc tp_compare;
reprfunc tp_repr;
/*
...
*/
} PyTypeObject;但我们需要注意一点,这些都是函数指针,我们去看一看destructor这个宏看看,tp_dealloc是一个什么类型。
typedef void (*destructor)(PyObject *);destructor是一个接受一个PyObject指针作为参数,并且无返回值的函数指针类型。接下来还有许多都是类似的声明,就不多说啦,都是类似的结构,至于每一种函数的作用目前我仍不能全说清楚。需要再多看一些其他类型的实现才能知道。
下一个部分。
typedef struct _typeobject {
/*
...
*/
/* Method suites for standard classes */
PyNumberMethods *tp_as_number;
PySequenceMethods *tp_as_sequence;
PyMappingMethods *tp_as_mapping;
/*
...
*/
} PyTypeObject;这三个指针就比较特别了,它们不是某一个函数指针,它们是结构体指针,指向啥呢?以PyNumberMethods举例。它里面包含了一堆函数,这些函数都是当我们使用这个PyObject的数值属性的时候会用到的函数。也就是tp_as_number的as_number的来由了。比如我们在python里输入1+1的时候,使用的就是nb_add进行操作了。
typedef struct {
binaryfunc nb_add;
binaryfunc nb_subtract;
binaryfunc nb_multiply;
binaryfunc nb_divide;
binaryfunc nb_remainder;
binaryfunc nb_divmod;
ternaryfunc nb_power;
/* 下面还有很多 ... */
} PyNumberMethods;总结
总结一发。在Python源码中,我们基本可以这么认为,基础对象(int,float,string,list,dict这些),它的对象构成是类似的,无非在PyObject或PyVarObject的基础上增加新的属性。而具体的方法的区别都体现在其type object所声明的函数指针的具体实现上。
什么意思呢?就是同样是nb_add,不同类型的对象很可能就会有不同的实现,而这一切都是随其type object的改变而改变。