美文网首页
大师兄的Python源码学习笔记(二十四): 虚拟机中的类机制(

大师兄的Python源码学习笔记(二十四): 虚拟机中的类机制(

作者: superkmi | 来源:发表于2021-07-15 21:24 被阅读0次

    大师兄的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对象存放在PyTypeObjecttp_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_ReadyPyTypeObject进行的改造:
    1. 设置type信息、基类及基类列表
    2. 填充tp_dict
    3. 确定mro列表
    4. 基于mro列表从基类继承操作
    5. 设置基类的子类列表

    相关文章

      网友评论

          本文标题:大师兄的Python源码学习笔记(二十四): 虚拟机中的类机制(

          本文链接:https://www.haomeiwen.com/subject/vjxfpltx.html