python源码剖析类机制_Python源码分析:类的机制

-- Illustrations by Daniel Liang --

作者:小屋子大侠,目前主要从事于python后端相关工作,使用使用python大概2年半的时间,平常喜欢分析工作中使用的工具的源码,如supervisor,gunicorn,django等,并编写了相应的源码分析博客,除了使用工具的分析外,对python的底层实现也有相应兴趣并编写过python源码有关的博客,目前主要学习于操作系统相关内容。个人博客地址:https://blog.csdn.net/qq_33339479/

本文主要分析Python中类时如何实现的,在Python中,一切都是对象;任何对象都有一个type,都可以通过class属性,一般情况下为type对应于Python源码中的PyTypeType;在Python的类中,都直接或者间接与Object有关联,都是Object的子类,对应Python中PyBaseObjectType。在Python的启动执行流程一文中有介绍,在Python启动的过程中会首先对默认的类型进行初始化,我们就从这里开始分析。

分析 初始化代码如下:void

_Py_ReadyTypes(void)

{

if (PyType_Ready(&PyType_Type) < 0)

Py_FatalError("Can't initialize 'type'");

if (PyType_Ready(&_PyWeakref_RefType) < 0)

Py_FatalError("Can't initialize 'weakref'");

if (PyType_Ready(&PyBool_Type) < 0)

Py_FatalError("Can't initialize 'bool'");

if (PyType_Ready(&PyString_Type) < 0)

Py_FatalError("Can't initialize 'str'");

if (PyType_Ready(&PyList_Type) < 0)

Py_FatalError("Can't initialize 'list'");

if (PyType_Ready(&PyNone_Type) < 0)

Py_FatalError("Can't initialize type(None)");

if (PyType_Ready(&PyNotImplemented_Type) < 0)

Py_FatalError("Can't initialize type(NotImplemented)");

}

这里分析下PyTypeReady(&PyTypeType)初始化过程,首先我们查看下PyType_Type类型的结构:PyTypeObject PyType_Type = {

PyObject_HEAD_INIT(&PyType_Type) // 类型还是PyType_Type

0, /* ob_size */

"type", /* tp_name */

sizeof(PyHeapTypeObject), /* tp_basicsize */

sizeof(PyMemberDef), /* tp_itemsize */

(destructor)type_dealloc, /* tp_dealloc */

0, /* tp_print */

0, /* tp_getattr */

0, /* tp_setattr */

type_compare, /* tp_compare */

(reprfunc)type_repr, /* tp_repr */

0, /* tp_as_number */

0, /* tp_as_sequence */

0, /* tp_as_mapping */

(hashfunc)_Py_HashPointer, /* tp_hash */ // 哈希函数

(ternaryfunc)type_call, /* tp_call */ // tp_call函数

0, /* tp_str */

(getattrofunc)type_getattro, /* tp_getattro */

(setattrofunc)type_setattro, /* tp_setattro */

0, /* tp_as_buffer */

Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |

Py_TPFLAGS_BASETYPE, /* tp_flags */

type_doc, /* tp_doc */

(traverseproc)type_traverse, /* tp_traverse */

(inquiry)type_clear, /* tp_clear */

0, /* tp_richcompare */

offsetof(PyTypeObject, tp_weaklist), /* tp_weaklistoffset */

0, /* tp_iter */

0, /* tp_iternext */

type_methods, /* tp_methods */

type_members, /* tp_members */

type_getsets, /* tp_getset */

0, /* tp_base */

0, /* tp_dict */

0, /* tp_descr_get */

0, /* tp_descr_set */

offsetof(PyTypeObject, tp_dict), /* tp_dictoffset */

0, /* tp_init */

0, /* tp_alloc */

type_new, /* tp_new */

PyObject_GC_Del, /* tp_free */

(inquiry)type_is_gc, /* tp_is_gc */

};

由此可见type定义中的tp_base为空,在初始化过程中就会将基类换成Object;我们看完定义后再查看初始化过程:int

PyType_Ready(PyTypeObject *type)

{

PyObject *dict, *bases;

PyTypeObject *base;

Py_ssize_t i, n;

if (type->tp_flags & Py_TPFLAGS_READY) {

assert(type->tp_dict != NULL);

return 0;

}

assert((type->tp_flags & Py_TPFLAGS_READYING) == 0);

type->tp_flags |= Py_TPFLAGS_READYING;

#ifdef Py_TRACE_REFS

/* PyType_Ready is the closest thing we have to a choke point

* for type objects, so is the best place I can think of to try

* to get type objects into the doubly-linked list of all objects.

* Still, not all type objects go thru PyType_Ready.

*/

_Py_AddToAllObjects((PyObject *)type, 0);

#endif

/* Initialize tp_base (defaults to BaseObject unless that's us) */

base = type->tp_base; // 获取初始化类型的基类

if (base == NULL && type != &PyBaseObject_Type) { // 如果基类为空,并且初始化的类型不为Object

base = type->tp_base = &PyBaseObject_Type; // 将初始化类的基类设置成Object

Py_INCREF(base);

}

/* Now the only way base can still be NULL is if type is

* &PyBaseObject_Type.

*/

/* Initialize the base class */

if (base && base->tp_dict == NULL) { // 如果基类的属性列表为空

if (PyType_Ready(base) < 0) // 初始化基类

goto error;

}

/* Initialize ob_type if NULL. This means extensions that want to be

compilable separately on Windows can call PyType_Ready() instead of

initializing the ob_type field of their type objects. */

/* The test for base != NULL is really unnecessary, since base is only

NULL when type is &PyBaseObject_Type, and we know its ob_type is

not NULL (it's initialized to &PyType_Type). But coverity doesn't

know that. */

if (type->ob_type == NULL && base != NULL) // 如果初始化类型的类型为空,并且基类不为空

type->ob_type = base->ob_type; // 初始化类型的类型设置成基类的类型

/* Initialize tp_bases */

bases = type->tp_bases; // 获取初始化类型的基类列表

if (bases == NULL) { // 如果基类列表为空

if (base == NULL) // 如果父类为空

bases = PyTuple_New(0); // 基类则生成一个空的元组

else

bases = PyTuple_Pack(1, base); // 如果基类不为空,生成长度为1的元组,并将base发入其中

if (bases == NULL)

goto error;

type->tp_bases = bases; // 将生成的bases设置到初始化类型中

}

/* Initialize tp_dict */

dict = type->tp_dict; // 获取类型的属性列表

if (dict == NULL) {

dict = PyDict_New(); // 如果属性为空,则生成一个字典,并设置到初始化类型中

if (dict == NULL)

goto error;

type->tp_dict = dict;

}

/* Add type-specific descriptors to tp_dict */

if (add_operators(type) < 0) // 给该类型添加描述方法

goto error;

if (type->tp_methods != NULL) {

if (add_methods(type, type->tp_methods) < 0) // 如果类型方法不为空,则将方法包装后添加到初始化类型中

goto error;

}

if (type->tp_members != NULL) {

if (add_members(type, type->tp_members) < 0) // 如果类型成员不为空,则将成员包装后添加到初始化类型中

goto error;

}

if (type->tp_getset != NULL) {

if (add_getset(type, type->tp_getset) < 0)

goto error;

}

/* Calculate method resolution order */

if (mro_internal(type) < 0) { // 获取初始化类型的基础列表

goto error;

}

/* Inherit special flags from dominant base */

if (type->tp_base != NULL)

inherit_special(type, type->tp_base); // 如果基类不为空,则继承基类的方法属性等

/* Initialize tp_dict properly */

bases = type->tp_mro; // 获取初始化类型的基础列表

assert(bases != NULL);

assert(PyTuple_Check(bases));

n = PyTuple_GET_SIZE(bases);

for (i = 1; i < n; i++) {

PyObject *b = PyTuple_GET_ITEM(bases, i); // 依次获取基础列表的值

if (PyType_Check(b))

inherit_slots(type, (PyTypeObject *)b); // 继承相应的方法

}

/* Sanity check for tp_free. */

if (PyType_IS_GC(type) && (type->tp_flags & Py_TPFLAGS_BASETYPE) &&

(type->tp_free == NULL || type->tp_free == PyObject_Del)) {

/* This base class needs to call tp_free, but doesn't have

* one, or its tp_free is for non-gc'ed objects.

*/

PyErr_Format(PyExc_TypeError, "type '%.100s' participates in "

"gc and is a base type but has inappropriate "

"tp_free slot",

type->tp_name);

goto error;

}

/* if the type dictionary doesn't contain a __doc__, set it from

the tp_doc slot.

*/

if (PyDict_GetItemString(type->tp_dict, "__doc__") == NULL) { // 设置属性的__doc__属性,如果有设置其中,如果没有则设置为空

if (type->tp_doc != NULL) {

PyObject *doc = PyString_FromString(type->tp_doc);

if (doc == NULL)

goto error;

PyDict_SetItemString(type->tp_dict, "__doc__", doc);

Py_DECREF(doc);

} else {

PyDict_SetItemString(type->tp_dict,

"__doc__", Py_None);

}

}

/* Some more special stuff */

base = type->tp_base; // 获取基类,如果子类相应的方法为空,则直接将基类方法设置给初始化类型

if (base != NULL) {

if (type->tp_as_number == NULL)

type->tp_as_number = base->tp_as_number;

if (type->tp_as_sequence == NULL)

type->tp_as_sequence = base->tp_as_sequence;

if (type->tp_as_mapping == NULL)

type->tp_as_mapping = base->tp_as_mapping;

if (type->tp_as_buffer == NULL)

type->tp_as_buffer = base->tp_as_buffer;

}

/* Link into each base class's list of subclasses */

bases = type->tp_bases; // 获取初始化类型的基类列表,

n = PyTuple_GET_SIZE(bases);

for (i = 0; i < n; i++) {

PyObject *b = PyTuple_GET_ITEM(bases, i);

if (PyType_Check(b) &&

add_subclass((PyTypeObject *)b, type) < 0) // 将初始化类型添加到基类中,填充基类子类列表

goto error;

}

/* All done -- set the ready flag */

assert(type->tp_dict != NULL);

type->tp_flags =

(type->tp_flags & ~Py_TPFLAGS_READYING) | Py_TPFLAGS_READY; // 设置该类型已经初始化完成

return 0;

error:

type->tp_flags &= ~Py_TPFLAGS_READYING;

return -1;

}

其中,主要经历了五个不中设置type信息,基类及基类列表;

填充tp_dict;

确定mro;

从mro列表继承基类属性;

设置基类的子类列表。 其中我们从初始化完成tp_dict后填充方法开始分析;/* Add type-specific descriptors to tp_dict */

if (add_operators(type) < 0) // 给该类型添加描述方法

goto error;

if (type->tp_methods != NULL) {

if (add_methods(type, type->tp_methods) < 0) // 如果类型方法不为空,则将方法包装后添加到初始化类型中

goto error;

}

if (type->tp_members != NULL) {

if (add_members(type, type->tp_members) < 0) // 如果类型成员不为空,则将成员包装后添加到初始化类型中

goto error;

}

if (type->tp_getset != NULL) {

if (add_getset(type, type->tp_getset) < 0)

goto error;

}

查看add_operators(type)方法;static int

add_operators(PyTypeObject *type)

{

PyObject *dict = type->tp_dict;

slotdef *p;

PyObject *descr;

void **ptr;

init_slotdefs(); // 排序slotdefs数组

for (p = slotdefs; p->name; p++) { // 获取slotdefs的值

if (p->wrapper == NULL) // 如果包装的函数为空,则直接获取下一个

continue;

ptr = slotptr(type, p->offset); // 转换获取相应的方法

if (!ptr || !*ptr)

continue;

if (PyDict_GetItem(dict, p->name_strobj)) // 如果在属性列表中获取到相应名称,则不覆盖直接下一个

continue;

descr = PyDescr_NewWrapper(type, p, *ptr); // 将该方法生成一个新的描述符

if (descr == NULL)

return -1;

if (PyDict_SetItem(dict, p->name_strobj, descr) < 0) // 将生成的描述符设置到对应的属性列表中

return -1;

Py_DECREF(descr);

}

if (type->tp_new != NULL) {

if (add_tp_new_wrapper(type) < 0)

return -1;

}

return 0;

}

这其中出现了init_slotdefs,这在Python中有一个叫slot,一个slot对应一个方法。其定义如下;typedef struct wrapperbase slotdef;

struct wrapperbase {

char *name; // 名称

int offset; // 偏移,相对于PyHead-TypeObject

void *function; // 包装的函数

wrapperfunc wrapper;

char *doc; // 文档

int flags;

PyObject *name_strobj; // 对应名称转换为字符串对象

};

对于slotdef,提供了多种宏来定义一个slotdef#define FLSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC, FLAGS) \

{NAME, offsetof(PyTypeObject, SLOT), (void *)(FUNCTION), WRAPPER, \

PyDoc_STR(DOC), FLAGS}

#define ETSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \

{NAME, offsetof(PyHeapTypeObject, SLOT), (void *)(FUNCTION), WRAPPER, \

PyDoc_STR(DOC)}

两个方法的不同就是一个相对于PyTypeObject,一个相对于PyHeapTypeObject,两个偏移量相对偏移的类型不同。其中PyHeapTypeObject定义如下;typedef struct _heaptypeobject {

/* Note: there's a dependency on the order of these members

in slotptr() in typeobject.c . */

PyTypeObject ht_type;

PyNumberMethods as_number;

PyMappingMethods as_mapping;

PySequenceMethods as_sequence; /* as_sequence comes after as_mapping,

so that the mapping wins when both

the mapping and the sequence define

a given operator (e.g. __getitem__).

see add_operators() in typeobject.c . */

PyBufferProcs as_buffer;

PyObject *ht_name, *ht_slots;

/* here are optional user slots, followed by the members. */

} PyHeapTypeObject;

为什么会有这个区别是因为在一个PyTypeObject对象中,对应的操作方法比如nbadd是存放在函数指针tpasnumber中,而这是另外一个结构,无法计算出nbadd相对于PyTypeObject的相对位置,由PyHeapTypeObject可以看出,可以直接计算相对位置。 接下来查看下slotdefs数组;static slotdef slotdefs[] = {

SQSLOT("__len__", sq_length, slot_sq_length, wrap_lenfunc,

"x.__len__() <==> len(x)"),

/* Heap types defining __add__/__mul__ have sq_concat/sq_repeat == NULL.

The logic in abstract.c always falls back to nb_add/nb_multiply in

this case. Defining both the nb_* and the sq_* slots to call the

user-defined methods has unexpected side-effects, as shown by

test_descr.notimplemented() */

SQSLOT("__add__", sq_concat, NULL, wrap_binaryfunc,

"x.__add__(y) <==> x+y"),

SQSLOT("__mul__", sq_repeat, NULL, wrap_indexargfunc,

"x.__mul__(n) <==> x*n"),

SQSLOT("__rmul__", sq_repeat, NULL, wrap_indexargfunc,

"x.__rmul__(n) <==> n*x"),

...

SQSLOT("__getitem__", sq_item, slot_sq_item, wrap_sq_item,

"x.__getitem__(y) <==> x[y]"),

...

MPSLOT("__getitem__", mp_subscript, slot_mp_subscript,

wrap_binaryfunc,

"x.__getitem__(y) <==> x[y]"),

...

}

其中,可以发现有不同名的操作,也有同名的操作,对应于同名的操作是如何做选择呢,此时如果是PyTypeObject,此时相对于PyHeapTypeObject,其中asmapping在assequence上面,所有mpsubscript的排序会比sqitem靠前,所以会选择mpsubscript。 此时继续查看initslotdefs代码;static void

init_slotdefs(void)

{

slotdef *p;

static int initialized = 0;

if (initialized)

return;

for (p = slotdefs; p->name; p++) {

p->name_strobj = PyString_InternFromString(p->name); // 将名称转换为Python内部字符串

if (!p->name_strobj)

Py_FatalError("Out of memory interning slotdef names");

}

qsort((void *)slotdefs, (size_t)(p-slotdefs), sizeof(slotdef),

slotdef_cmp); // 按照slot_cmp函数规则排序

initialized = 1;

}

然后执行如下代码;for (p = slotdefs; p->name; p++) { // 获取slotdefs的值

if (p->wrapper == NULL) // 如果包装的函数为空,则直接获取下一个

continue;

ptr = slotptr(type, p->offset); // 转换获取相应的方法

if (!ptr || !*ptr)

continue;

if (PyDict_GetItem(dict, p->name_strobj)) // 如果在属性列表中获取到相应名称,则不覆盖直接下一个

continue;

descr = PyDescr_NewWrapper(type, p, *ptr); // 将该方法生成一个新的描述符

if (descr == NULL)

return -1;

if (PyDict_SetItem(dict, p->name_strobj, descr) < 0) // 将生成的描述符设置到对应的属性列表中

return -1;

这里可以看到当需要在tpdict中设置属性时,并不是直接向slotdef直接设置到属性中,而是通过PyDescrNewWrapper生成一个描述符放置在tpdict中,为何要这么设计呢? 我们先查看PyDescrNewWrapper;PyObject *

PyDescr_NewWrapper(PyTypeObject *type, struct wrapperbase *base, void *wrapped)

{

PyWrapperDescrObject *descr;

descr = (PyWrapperDescrObject *)descr_new(&PyWrapperDescr_Type,

type, base->name);

if (descr != NULL) {

descr->d_base = base; // slotdef

descr->d_wrapped = wrapped; // 被包装的函数

}

return (PyObject *)descr;

}

继续查看descr_new;static PyDescrObject *

descr_new(PyTypeObject *descrtype, PyTypeObject *type, const char *name)

{

PyDescrObject *descr;

descr = (PyDescrObject *)PyType_GenericAlloc(descrtype, 0);

if (descr != NULL) {

Py_XINCREF(type);

descr->d_type = type; // 设置装饰的类型

descr->d_name = PyString_InternFromString(name); // 设置被装饰的名称

if (descr->d_name == NULL) {

Py_DECREF(descr);

descr = NULL;

}

}

return descr;

}

由此查看PyDescrObject类型;#define PyDescr_COMMON \

PyObject_HEAD \

PyTypeObject *d_type; \

PyObject *d_name

typedef struct {

PyDescr_COMMON;

struct wrapperbase *d_base;

void *d_wrapped; /* This can be any function pointer */

} PyWrapperDescrObject;

由此可知一个descr是一个type类型的对象,为什么要转换为一个type类型呢,当调用tpdict的方法时,需要调用其中tpcall方法,而slotdef并不是一个可调用对象,并不符合可调用的要求。 在以上流程中,比较重要的就是怎样获取对应的ptr然后生成wrapper存入tp_dict中,ptr = slotptr(type, p->offset); // 转换获取相应的方法

对应的方法为static void **

slotptr(PyTypeObject *type, int ioffset)

{

char *ptr;

long offset = ioffset;

/* Note: this depends on the order of the members of PyHeapTypeObject! */

assert(offset >= 0);

assert((size_t)offset < offsetof(PyHeapTypeObject, as_buffer));

if ((size_t)offset >= offsetof(PyHeapTypeObject, as_sequence)) {

ptr = (char *)type->tp_as_sequence;

offset -= offsetof(PyHeapTypeObject, as_sequence);

}

else if ((size_t)offset >= offsetof(PyHeapTypeObject, as_mapping)) {

ptr = (char *)type->tp_as_mapping;

offset -= offsetof(PyHeapTypeObject, as_mapping);

}

else if ((size_t)offset >= offsetof(PyHeapTypeObject, as_number)) {

ptr = (char *)type->tp_as_number;

offset -= offsetof(PyHeapTypeObject, as_number);

}

else {

ptr = (char *)type;

}

if (ptr != NULL)

ptr += offset;

return (void **)ptr;

}

此时,从上至下,依次是PyHeapTypeObject对应的assequence,asmapping,asnumber,这是因为offset的偏移是从大到小进行检查,因为assequence的距离是最远的,如果比assequence还小则再检查asmapping,如果比asmapping还小则检查asnumber,这样就可以查找出对应偏移量对应的方法。 此时tpdict属性字典方法填充完成。 当tpdict完成后类继承相关的操作 由于Python中的类的基础是一句传入的参数依次从左至右开始继承;>>> class A(object):

... pass

...

>>> class B(object):

... pass

...

>>> A.__mro__

(, )

>>> class C(B,A):

... pass

...

>>> class D(C,A):

... pass

...

>>> D.__mro__

(, , , , )

>>> D.__bases__

(, )

>>> C.__mro__

(, , , )

>>>

可见D的继承顺序是从左至右,依次继承初始化, 继承的代码执行流程如下;/* Initialize tp_dict properly */

bases = type->tp_mro; // 获取初始化类型的基础列表

assert(bases != NULL);

assert(PyTuple_Check(bases));

n = PyTuple_GET_SIZE(bases);

for (i = 1; i < n; i++) {

PyObject *b = PyTuple_GET_ITEM(bases, i); // 依次获取基础列表的值

if (PyType_Check(b))

inherit_slots(type, (PyTypeObject *)b); // 继承相应的方法

}

由例子可知,tpmro的第一项是自身,所以i=1开始,依次获取对应的基类,然后执行,inheritslots,该函数主要就是检查基类中对应的方法子类中是否拥有,如果没有拥有则拷贝到子类中对应的方法中去,以此达到继承父类方法的功能,主要是一些方法的拷贝有兴趣可自行查看。 接着就将子类添加到基类的子类列表中bases = type->tp_bases; // 获取初始化类型的基类列表,

n = PyTuple_GET_SIZE(bases);

for (i = 0; i < n; i++) {

PyObject *b = PyTuple_GET_ITEM(bases, i);

if (PyType_Check(b) &&

add_subclass((PyTypeObject *)b, type) < 0) // 将初始化类型添加到基类中,填充基类子类列表

goto error;

}

其中调用add_subclass时还生成了Python中的引用相关的操作,有兴趣课自行查看。 至此初始化就完成,一个PyTypeObject的初始化工作就已经完成,初始化内置类型的操作流程基本分析完成。

本文环境:Python2.5系列

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值