大师兄的Python源码学习笔记(二十三): 虚拟机中的类机制(二)
大师兄的Python源码学习笔记(二十五): 虚拟机中的类机制(四)
二. 从type对象到class对象
3. 填充tp_dict
3.2 确定MRO
- 由于Python支持多重继承,因此在多重继承时需要判断以何种顺序解析属性。
- Method Resolve Order(MRO)即Class对象属性的解析顺序。
>>>class A(list):
>>> def show(self):
>>> print("A::show")
>>>
>>>class B(list):
>>> def show(self):
>>> print("B::show")
>>>
>>>class C(A):
>>> pass
>>>
>>>class D(C,B):
>>> pass
>>>
>>>if __name__ == '__main__':
>>> d = D()
>>> d.show()
A::show
1 0 LOAD_BUILD_CLASS
2 LOAD_CONST 0 (<code object A at 0x0000021737C39150, file "D:\pythonProject\parser_learn\demo.py", line 1>)
4 LOAD_CONST 1 ('A')
6 MAKE_FUNCTION 0
8 LOAD_CONST 1 ('A')
10 LOAD_NAME 0 (list)
12 CALL_FUNCTION 3
14 STORE_NAME 1 (A)
5 16 LOAD_BUILD_CLASS
18 LOAD_CONST 2 (<code object B at 0x0000021737C39030, file "D:\pythonProject\parser_learn\demo.py", line 5>)
20 LOAD_CONST 3 ('B')
22 MAKE_FUNCTION 0
24 LOAD_CONST 3 ('B')
26 LOAD_NAME 0 (list)
28 CALL_FUNCTION 3
30 STORE_NAME 2 (B)
9 32 LOAD_BUILD_CLASS
34 LOAD_CONST 4 (<code object C at 0x0000021737C39540, file "D:\pythonProject\parser_learn\demo.py", line 9>)
36 LOAD_CONST 5 ('C')
38 MAKE_FUNCTION 0
40 LOAD_CONST 5 ('C')
42 LOAD_NAME 1 (A)
44 CALL_FUNCTION 3
46 STORE_NAME 3 (C)
12 48 LOAD_BUILD_CLASS
50 LOAD_CONST 6 (<code object D at 0x0000021737C395D0, file "D:\pythonProject\parser_learn\demo.py", line 12>)
52 LOAD_CONST 7 ('D')
54 MAKE_FUNCTION 0
56 LOAD_CONST 7 ('D')
58 LOAD_NAME 3 (C)
60 LOAD_NAME 2 (B)
62 CALL_FUNCTION 4
64 STORE_NAME 4 (D)
15 66 LOAD_NAME 5 (__name__)
68 LOAD_CONST 8 ('__main__')
70 COMPARE_OP 2 (==)
72 POP_JUMP_IF_FALSE 88
16 74 LOAD_NAME 4 (D)
76 CALL_FUNCTION 0
78 STORE_NAME 6 (d)
17 80 LOAD_NAME 6 (d)
82 LOAD_METHOD 7 (show)
84 CALL_METHOD 0
86 POP_TOP
>> 88 LOAD_CONST 9 (None)
90 RETURN_VALUE
- 这里实际是通过PyType_Ready中通过mro_internal函数完成了对一个类型的mro顺序的建立:
Objects\typeobject.c
int
PyType_Ready(PyTypeObject *type)
{
... ...
/* Calculate method resolution order */
if (mro_internal(type, NULL) < 0)
goto error;
... ...
}
Objects\typeobject.c
static int
mro_internal(PyTypeObject *type, PyObject **p_old_mro)
{
PyObject *new_mro, *old_mro;
int reent;
/* Keep a reference to be able to do a reentrancy check below.
Don't let old_mro be GC'ed and its address be reused for
another object, like (suddenly!) a new tp_mro. */
old_mro = type->tp_mro;
Py_XINCREF(old_mro);
new_mro = mro_invoke(type); /* might cause reentrance */
reent = (type->tp_mro != old_mro);
Py_XDECREF(old_mro);
if (new_mro == NULL)
return -1;
if (reent) {
Py_DECREF(new_mro);
return 0;
}
type->tp_mro = new_mro;
type_mro_modified(type, type->tp_mro);
/* corner case: the super class might have been hidden
from the custom MRO */
type_mro_modified(type, type->tp_bases);
PyType_Modified(type);
if (p_old_mro != NULL)
*p_old_mro = old_mro; /* transfer the ownership */
else
Py_XDECREF(old_mro);
return 1;
}
- Python虚拟机将创建一个tuple对象,按照虚拟机解析属性时的mro顺序存放class对象,并将这个tuple对象存放在PyTypeObject的tp_mro中:
Include\object.h
typedef struct _typeobject {
... ...
PyObject *tp_mro; /* method resolution order */
... ...
} PyTypeObject;
- 可以在Python中通过
Class.__mro__
函数查看tp_mro:
>>>class A(list):
>>> def show(self):
>>> print("A::show")
>>>
>>>class B(list):
>>> def show(self):
>>> print("B::show")
>>>
>>>class C(A):
>>> pass
>>>
>>>class D(C,B):
>>> pass
>>>
>>>if __name__ == '__main__':
>>> print(D.__mro__)
(<class '__main__.D'>, <class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'list'>, <class 'object'>)
3.3 继承基类操作
- Python虚拟机确定mro列表后,会遍历mro列表。
- 由于第一项总是其自身,所以遍历会从第二项开始。
- mro列表中存储的是Class对象的所有直接和间接基类,虚拟机会将Class对象自身没有设置而积累中设置了的操作复制到Class对象中,从而完成基类操作的继承动作:
Objects\typeobject.c
int
PyType_Ready(PyTypeObject *type)
{
... ...
/* 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);
}
}
Objects\typeobject.c
static void
inherit_slots(PyTypeObject *type, PyTypeObject *base)
{
PyTypeObject *basebase;
#undef SLOTDEFINED
#undef COPYSLOT
#undef COPYNUM
#undef COPYSEQ
#undef COPYMAP
#undef COPYBUF
#define SLOTDEFINED(SLOT) \
(base->SLOT != 0 && \
(basebase == NULL || base->SLOT != basebase->SLOT))
#define COPYSLOT(SLOT) \
if (!type->SLOT && SLOTDEFINED(SLOT)) type->SLOT = base->SLOT
#define COPYASYNC(SLOT) COPYSLOT(tp_as_async->SLOT)
#define COPYNUM(SLOT) COPYSLOT(tp_as_number->SLOT)
#define COPYSEQ(SLOT) COPYSLOT(tp_as_sequence->SLOT)
#define COPYMAP(SLOT) COPYSLOT(tp_as_mapping->SLOT)
#define COPYBUF(SLOT) COPYSLOT(tp_as_buffer->SLOT)
/* This won't inherit indirect slots (from tp_as_number etc.)
if type doesn't provide the space. */
if (type->tp_as_number != NULL && base->tp_as_number != NULL) {
basebase = base->tp_base;
if (basebase->tp_as_number == NULL)
basebase = NULL;
COPYNUM(nb_add);
COPYNUM(nb_subtract);
COPYNUM(nb_multiply);
COPYNUM(nb_remainder);
... ...
}
3.4 填充基类中的子类列表
- 最后,PyType_Ready还要设置基类中的子类列表。
- 在每一个PyTypeObject中的tp_subclasses是一个list对象,其中存放着所有直接继承自该类型的class对象。
- PyType_Ready通过调用add_subclass完成向tp_subclasses中填充对象的动作。
Objects\typeobject.c
int
PyType_Ready(PyTypeObject *type)
{
PyObject *dict, *bases;
PyTypeObject *base;
Py_ssize_t i, n;
... ...
/* 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;
}
... ...
}
Objects\typeobject.c
static int
add_subclass(PyTypeObject *base, PyTypeObject *type)
{
int result = -1;
PyObject *dict, *key, *newobj;
dict = base->tp_subclasses;
if (dict == NULL) {
base->tp_subclasses = dict = PyDict_New();
if (dict == NULL)
return -1;
}
assert(PyDict_CheckExact(dict));
key = PyLong_FromVoidPtr((void *) type);
if (key == NULL)
return -1;
newobj = PyWeakref_NewRef((PyObject *)type, NULL);
if (newobj != NULL) {
result = PyDict_SetItem(dict, key, newobj);
Py_DECREF(newobj);
}
Py_DECREF(key);
return result;
}
- 在Python中可以通过
class.__subclass__
查看tp_subclasses
>>>class A(list):
>>> def show(self):
>>> print("A::show")
>>>
>>>class B(list):
>>> def show(self):
>>> print("B::show")
>>>
>>>class C(A):
>>> pass
>>>
>>>class D(C,B):
>>> pass
>>>
>>>if __name__ == '__main__':
>>> print(C.__subclasses__())
[<class '__main__.D'>]
4. 总结
- 总结PyType_Ready对PyTypeObject进行的改造:
- 设置type信息、基类及基类列表
- 填充tp_dict
- 确定mro列表
- 基于mro列表从基类继承操作
- 设置基类的子类列表