-- 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;
}
其中,主要经历了五个不中
/* 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 '__main__.A'>, <type 'object'>)
>>> class C(B,A):
... pass
...
>>> class D(C,A):
... pass
...
>>> D.__mro__
(<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <type 'object'>)
>>> D.__bases__
(<class '__main__.C'>, <class '__main__.A'>)
>>> C.__mro__
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <type 'object'>)
>>>
可见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系列