Python源码学习:Python类机制分析-用户自定义类
发布日期:2021-07-25 13:04:29 浏览次数:11 分类:技术文章

本文共 45161 字,大约阅读时间需要 150 分钟。

Python源码分析

本文环境python2.5系列参考书籍<
>

上一文,分析了Python在启动初始化时,对内置类的一个基本的初始化流程,本文就简析一下用户自定义类的实现过程。

分析

脚本如下;

class A(object):    name = 'attr_a'    def __init__(self):        print("A.__init__")    def f(self):        print("A.f")    def g(self, aValue):        self.aValue = aValue        print(self.aValue)        print("A.g")   a = A()a.f()a.g(5)

根据该脚本代码可知,该脚本包含两段PyCodeObject,脚本对应的字节码如下;

1           0 LOAD_CONST               0 ('A')              3 LOAD_NAME                0 (object)              6 BUILD_TUPLE              1              9 LOAD_CONST               1 ()             12 MAKE_FUNCTION            0             15 CALL_FUNCTION            0             18 BUILD_CLASS                      19 STORE_NAME               1 (A) 15          22 LOAD_NAME                1 (A)             25 CALL_FUNCTION            0             28 STORE_NAME               2 (a) 16          31 LOAD_NAME                2 (a)             34 LOAD_ATTR                3 (f)             37 CALL_FUNCTION            0             40 POP_TOP              17          41 LOAD_NAME                2 (a)             44 LOAD_ATTR                4 (g)             47 LOAD_CONST               2 (5)             50 CALL_FUNCTION            1             53 POP_TOP                          54 LOAD_CONST               3 (None)             57 RETURN_VALUE

其中类A对应的字节码如下;

1           0 LOAD_GLOBAL              0 (__name__)              3 STORE_NAME               1 (__module__)  2           6 LOAD_CONST               1 ('attr_a')              9 STORE_NAME               2 (name)  4          12 LOAD_CONST               2 ()             15 MAKE_FUNCTION            0             18 STORE_NAME               3 (__init__)  7          21 LOAD_CONST               3 ()             24 MAKE_FUNCTION            0             27 STORE_NAME               4 (f) 10          30 LOAD_CONST               4 ()             33 MAKE_FUNCTION            0             36 STORE_NAME               5 (g)             39 LOAD_LOCALS                      40 RETURN_VALUE

其中,类A中对应的三个函数对应的字节码如下;

Disassembly of __init__:  4           0 LOAD_CONST               1 ('A.__init__')              3 PRINT_ITEM                        4 PRINT_NEWLINE                     5 LOAD_CONST               0 (None)              8 RETURN_VALUE        Disassembly of f:  6           0 LOAD_CONST               1 ('A.f')              3 PRINT_ITEM                        4 PRINT_NEWLINE                     5 LOAD_CONST               0 (None)              8 RETURN_VALUE        Disassembly of g:  8           0 LOAD_FAST                1 (aValue)              3 LOAD_FAST                0 (self)              6 STORE_ATTR               0 (aValue)  9           9 LOAD_FAST                0 (self)             12 LOAD_ATTR                0 (aValue)             15 PRINT_ITEM                       16 PRINT_NEWLINE        10          17 LOAD_CONST               1 ('A.g')             20 PRINT_ITEM                       21 PRINT_NEWLINE                    22 LOAD_CONST               0 (None)             25 RETURN_VALUE

首先先看下脚本执行的字节码文件,执行流程如下;

1           0 LOAD_CONST               0 ('A')              3 LOAD_NAME                0 (object)              6 BUILD_TUPLE              1              9 LOAD_CONST               1 ()

先将A加载到常量中,然后建立了一个元素大小的元组并将object放入其中,并将新建的元组压栈,然后将A对应的字节码压栈,执行完之后栈中的布局就变为如下所示;

|  A            || tuple(object) || code A        |

然后继续执行字节码;

12 MAKE_FUNCTION            0             15 CALL_FUNCTION            0             18 BUILD_CLASS                      19 STORE_NAME               1 (A)

此时,通过分析函数调用的过程可知,MAKE_FUNCTION将对应的code A包装成一个PyFunctionObject,然后调用CALL_FUNCTION执行对应的code A中的字节码,由此查看类A对应的字节码;

1           0 LOAD_GLOBAL              0 (__name__)              3 STORE_NAME               1 (__module__)  2           6 LOAD_CONST               1 ('attr_a')              9 STORE_NAME               2 (name)  4          12 LOAD_CONST               2 ()             15 MAKE_FUNCTION            0             18 STORE_NAME               3 (__init__)  7          21 LOAD_CONST               3 ()             24 MAKE_FUNCTION            0             27 STORE_NAME               4 (f) 10          30 LOAD_CONST               4 ()             33 MAKE_FUNCTION            0             36 STORE_NAME               5 (g)             39 LOAD_LOCALS                      40 RETURN_VALUE

此时,code A中对应的names列表如下;

('__name__', '__module__', 'name', '__init__', 'f', 'g')

先执行了LOAD_GLOBAL设置name,接着就将name存储到module中,然后加载常量name,然后加载函数init,加载f,加载g,此时每一次都通过STORE_NAME压入,

case STORE_NAME:            w = GETITEM(names, oparg);      // 从变量列表中获取对应的变量            v = POP();            if ((x = f->f_locals) != NULL) {                if (PyDict_CheckExact(x))                    err = PyDict_SetItem(x, w, v);    // 检查locals是是否字典类型,如果是字典类型则将对应的name与结果设置到字典中                else                    err = PyObject_SetItem(x, w, v);                Py_DECREF(v);                if (err == 0) continue;                break;            }            PyErr_Format(PyExc_SystemError,                     "no locals found when storing %s",                     PyObject_REPR(w));            break;

由此可知,在进过STORE_NAME之后,就将class A中对应的定义过的方法属性等信息保存了起来,当执行到最后两步时,

39 LOAD_LOCALS                      40 RETURN_VALUE

调用LOAD_LOCALS时,

case LOAD_LOCALS:            if ((x = f->f_locals) != NULL) {                Py_INCREF(x);                PUSH(x);                continue;            }            PyErr_SetString(PyExc_SystemError, "no locals");            break;

将帧对应的变量列表压栈,此时的x就是STORE_NAME中压入的x,然后将x作为值返回后,我们继续查看脚本文件的字节码执行,此时脚本字节码如下;

18 BUILD_CLASS                      19 STORE_NAME               1 (A)

此时我们查看BUILD_CLASS的代码;

case BUILD_CLASS:            u = TOP();                //栈中第三个位置的值,此时就是调用call_function后,生成的f-locals中的字典             v = SECOND();             // 栈中的第二个位置的值,此时对应一个包含object的元组            w = THIRD();              // 压入的栈顶值,此时对应A            STACKADJ(-2);             // 将栈置顶            x = build_class(u, v, w);  // 创建class            SET_TOP(x);               // 将v设置到栈低一位的位置上            Py_DECREF(u);            Py_DECREF(v);            Py_DECREF(w);            break;

我们继续查看build_class的执行过程;

static PyObject *build_class(PyObject *methods, PyObject *bases, PyObject *name){    PyObject *metaclass = NULL, *result, *base;    if (PyDict_Check(methods))        metaclass = PyDict_GetItemString(methods, "__metaclass__");   // 如果用户定义了__metaclass__则使用用户自己定义的    if (metaclass != NULL)        Py_INCREF(metaclass);    else if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) {   // 检查bases是否是大于0大小的元组        base = PyTuple_GET_ITEM(bases, 0);                            // 获取元组中索引值为0 的基类        metaclass = PyObject_GetAttrString(base, "__class__");        // 获取base中的__class__属性        if (metaclass == NULL) {
// 如果还是没有获取到元类 PyErr_Clear(); metaclass = (PyObject *)base->ob_type; // 则设置type为该类的元类 Py_INCREF(metaclass); } } else { PyObject *g = PyEval_GetGlobals(); // 获取全局的变量 if (g != NULL && PyDict_Check(g)) metaclass = PyDict_GetItemString(g, "__metaclass__"); // 如果全局变量字典中,获取元类 if (metaclass == NULL) metaclass = (PyObject *) &PyClass_Type; // 如果任然没有获取到元类信息,则是classobj作为元类 Py_INCREF(metaclass); } result = PyObject_CallFunctionObjArgs(metaclass, name, bases, methods, NULL); // 调用该函数创建类 Py_DECREF(metaclass); ... return result;}

此时就是先获取要创建类的元类,然后调用PyObject_CallFunctionObjArgs将参数传入生成类;继续查看PyObject_CallFunctionObjArgs执行过程;

PyObject *PyObject_CallFunctionObjArgs(PyObject *callable, ...){    PyObject *args, *tmp;    va_list vargs;    if (callable == NULL)        return null_error();    /* count the args */    va_start(vargs, callable);    // 获取传入的变长参宿    args = objargs_mktuple(vargs);  // 将变长参数转变成一个元组    va_end(vargs);                  if (args == NULL)        return NULL;    tmp = PyObject_Call(callable, args, NULL);  // 调用该函数    Py_DECREF(args);    return tmp;}

继续查看PyObject_Call代码;

PyObject *PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw){        ternaryfunc call;    if ((call = func->ob_type->tp_call) != NULL) {                 PyObject *result = (*call)(func, arg, kw);     // 此时调用func->ob_type的tp_call方法        if (result == NULL && !PyErr_Occurred())            PyErr_SetString(                PyExc_SystemError,                "NULL result without error in PyObject_Call");        return result;    }    PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",             func->ob_type->tp_name);    return NULL;}

此时由于传入的基类时object类,所以调用的是object类中的ob_type->tp_call,此时的ob_type为type,type的tp_call如下;

PyTypeObject PyType_Type = {    PyObject_HEAD_INIT(&PyType_Type)    0,                  /* ob_size */    "type",                 /* tp_name */    sizeof(PyHeapTypeObject),       /* tp_basicsize */    sizeof(PyMemberDef),            /* tp_itemsize */    (destructor)type_dealloc,       /* tp_dealloc */    ...    (ternaryfunc)type_call,         /* tp_call */    0,                  /* tp_str */    (getattrofunc)type_getattro,        /* tp_getattro */    (setattrofunc)type_setattro,        /* tp_setattro */    ...    type_methods,               /* tp_methods */    type_members,               /* tp_members */    type_getsets,               /* tp_getset */    ...    offsetof(PyTypeObject, tp_dict),    /* tp_dictoffset */    ...    type_new,               /* tp_new */    ...};

type_call,此时我们查看type_call中进行了什么操作;

static PyObject *type_call(PyTypeObject *type, PyObject *args, PyObject *kwds){    PyObject *obj;    if (type->tp_new == NULL) {                          // 检查type的tp_new是否为空,为空则报错        PyErr_Format(PyExc_TypeError,                 "cannot create '%.100s' instances",                 type->tp_name);        return NULL;    }    obj = type->tp_new(type, args, kwds);           // 调用type的tp_new方法来新建obj    if (obj != NULL) {        /* Ugly exception: when the call was type(something),           don't call tp_init on the result. */        if (type == &PyType_Type &&            PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1 &&            (kwds == NULL ||             (PyDict_Check(kwds) && PyDict_Size(kwds) == 0)))            return obj;        /* If the returned object is not an instance of type,           it won't be initialized. */        if (!PyType_IsSubtype(obj->ob_type, type))            return obj;        type = obj->ob_type;        if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_CLASS) &&            type->tp_init != NULL &&            type->tp_init(obj, args, kwds) < 0) {       // 如果不是新建而是创建实例,还调用tp_init方法            Py_DECREF(obj);            obj = NULL;        }    }    return obj;}

此时,调用的是type的tp_new,由上一段代码片段可知,调用了PyType_Type中的type_new方法;

static PyObject *type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds){    PyObject *name, *bases, *dict;    static char *kwlist[] = {
"name", "bases", "dict", 0}; PyObject *slots, *tmp, *newslots; PyTypeObject *type, *base, *tmptype, *winner; PyHeapTypeObject *et; PyMemberDef *mp; Py_ssize_t i, nbases, nslots, slotoffset, add_dict, add_weak; int j, may_add_dict, may_add_weak; assert(args != NULL && PyTuple_Check(args)); assert(kwds == NULL || PyDict_Check(kwds)); /* Special case: type(x) should return x->ob_type */ { const Py_ssize_t nargs = PyTuple_GET_SIZE(args); const Py_ssize_t nkwds = kwds == NULL ? 0 : PyDict_Size(kwds); if (PyType_CheckExact(metatype) && nargs == 1 && nkwds == 0) { PyObject *x = PyTuple_GET_ITEM(args, 0); Py_INCREF(x->ob_type); return (PyObject *) x->ob_type; } /* SF bug 475327 -- if that didn't trigger, we need 3 arguments. but PyArg_ParseTupleAndKeywords below may give a msg saying type() needs exactly 3. */ if (nargs + nkwds != 3) { PyErr_SetString(PyExc_TypeError, "type() takes 1 or 3 arguments"); return NULL; } } /* Check arguments: (name, bases, dict) */ if (!PyArg_ParseTupleAndKeywords(args, kwds, "SO!O!:type", kwlist, &name, &PyTuple_Type, &bases, &PyDict_Type, &dict)) // 将传入的参数依次解析到 name bases dict三个变量中 return NULL; /* Determine the proper metatype to deal with this, and check for metatype conflicts while we're at it. Note that if some other metatype wins to contract, it's possible that its instances are not types. */ nbases = PyTuple_GET_SIZE(bases); // 获取基类列表的大小 winner = metatype; // 默认设置传入的为最佳元类 for (i = 0; i < nbases; i++) { tmp = PyTuple_GET_ITEM(bases, i); // 依次遍历基类列表 tmptype = tmp->ob_type; // 获取基类的类型 if (tmptype == &PyClass_Type) // 如果获取的基类是classobj则跳过 continue; /* Special case classic classes */ if (PyType_IsSubtype(winner, tmptype)) // 检查winner tmptype是否是子类,获取是父类选择最佳类型 continue; if (PyType_IsSubtype(tmptype, winner)) { winner = tmptype; continue; } PyErr_SetString(PyExc_TypeError, "metaclass conflict: " "the metaclass of a derived class " "must be a (non-strict) subclass " "of the metaclasses of all its bases"); return NULL; } if (winner != metatype) { // 如果比较之后的类不是传入的最初设置的类则将比较后的值设置到metatype if (winner->tp_new != type_new) /* Pass it to the winner */ return winner->tp_new(winner, args, kwds); // 如果比较之后的winner的tp_new不是type_new方法,则调用设置后的方法 metatype = winner; } /* Adjust for empty tuple bases */ if (nbases == 0) { // 如果基类列表为空 bases = PyTuple_Pack(1, &PyBaseObject_Type); // 设置基类列表的基类为object if (bases == NULL) return NULL; nbases = 1; } else Py_INCREF(bases); /* XXX From here until type is allocated, "return NULL" leaks bases! */ /* Calculate best base, and check that all bases are type objects */ base = best_base(bases); // 比较获取最佳的父类 if (base == NULL) { Py_DECREF(bases); return NULL; } if (!PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) { PyErr_Format(PyExc_TypeError, "type '%.100s' is not an acceptable base type", base->tp_name); Py_DECREF(bases); return NULL; } /* Check for a __slots__ sequence variable in dict, and count it */ slots = PyDict_GetItemString(dict, "__slots__"); // 获取属性字典中是否有__slots__值存在,该值是限制类中有什么属性 nslots = 0; add_dict = 0; add_weak = 0; may_add_dict = base->tp_dictoffset == 0; // __dict__相对于类的偏移 may_add_weak = base->tp_weaklistoffset == 0 && base->tp_itemsize == 0; // 弱引用列表 if (slots == NULL) { // 如果为空则全部置1 if (may_add_dict) { add_dict++; } if (may_add_weak) { add_weak++; } } else { /* Have slots */ /* Make it into a tuple */ if (PyString_Check(slots)) // 如果是字符串,则转换成包含一个元素的元组 slots = PyTuple_Pack(1, slots); else slots = PySequence_Tuple(slots); // 如果是列表,则转换成元组 if (slots == NULL) { Py_DECREF(bases); return NULL; } assert(PyTuple_Check(slots)); // 检查是否转换成元组 /* Are slots allowed? */ nslots = PyTuple_GET_SIZE(slots); // 获取元组的大小 if (nslots > 0 && base->tp_itemsize != 0) { PyErr_Format(PyExc_TypeError, "nonempty __slots__ " "not supported for subtype of '%s'", base->tp_name); bad_slots: Py_DECREF(bases); Py_DECREF(slots); return NULL; }#ifdef Py_USING_UNICODE tmp = _unicode_to_string(slots, nslots); // 进行字符集转换 if (tmp != slots) { Py_DECREF(slots); slots = tmp; } if (!tmp) return NULL;#endif /* Check for valid slot names and two special cases */ for (i = 0; i < nslots; i++) { PyObject *tmp = PyTuple_GET_ITEM(slots, i); // 依次获取对应的字符串 char *s; if (!valid_identifier(tmp)) // 检查字符串是否合法 goto bad_slots; assert(PyString_Check(tmp)); s = PyString_AS_STRING(tmp); // 获取值 if (strcmp(s, "__dict__") == 0) { // 如果是__dict__,则add_dict加1 if (!may_add_dict || add_dict) { PyErr_SetString(PyExc_TypeError, "__dict__ slot disallowed: " "we already got one"); goto bad_slots; } add_dict++; } if (strcmp(s, "__weakref__") == 0) { // 如果是__weakref__,则add_weak加1 if (!may_add_weak || add_weak) { PyErr_SetString(PyExc_TypeError, "__weakref__ slot disallowed: " "either we already got one, " "or __itemsize__ != 0"); goto bad_slots; } add_weak++; } } /* Copy slots into yet another tuple, demangling names */ newslots = PyTuple_New(nslots - add_dict - add_weak); // 因为不能创建__dict__和__weakref__,所有要减去这个字符串的数量,新建新的元组 if (newslots == NULL) goto bad_slots; for (i = j = 0; i < nslots; i++) { char *s; tmp = PyTuple_GET_ITEM(slots, i); s = PyString_AS_STRING(tmp); if ((add_dict && strcmp(s, "__dict__") == 0) || (add_weak && strcmp(s, "__weakref__") == 0)) continue; tmp =_Py_Mangle(name, tmp); if (!tmp) goto bad_slots; PyTuple_SET_ITEM(newslots, j, tmp); // 如果slots中的字符串不是__dict__和__weakref__,则设置到新的元组中 j++; } assert(j == nslots - add_dict - add_weak); nslots = j; Py_DECREF(slots); slots = newslots; // 将slots重置 /* Secondary bases may provide weakrefs or dict */ if (nbases > 1 && ((may_add_dict && !add_dict) || (may_add_weak && !add_weak))) { for (i = 0; i < nbases; i++) { tmp = PyTuple_GET_ITEM(bases, i); if (tmp == (PyObject *)base) continue; /* Skip primary base */ if (PyClass_Check(tmp)) { /* Classic base class provides both */ if (may_add_dict && !add_dict) add_dict++; if (may_add_weak && !add_weak) add_weak++; break; } assert(PyType_Check(tmp)); tmptype = (PyTypeObject *)tmp; if (may_add_dict && !add_dict && tmptype->tp_dictoffset != 0) add_dict++; if (may_add_weak && !add_weak && tmptype->tp_weaklistoffset != 0) add_weak++; if (may_add_dict && !add_dict) continue; if (may_add_weak && !add_weak) continue; /* Nothing more to check */ break; } } } /* XXX From here until type is safely allocated, "return NULL" may leak slots! */ /* Allocate the type object */ type = (PyTypeObject *)metatype->tp_alloc(metatype, nslots); // 调用比较后的元类进行,申请内存大小,如果传入的是Object,则是调用了type中的tp_alloc if (type == NULL) { // type中的tp_alloc是获取tp_basicsize+tp_itemsize,此时type中的大小sizeof(PyHeapTypeObject)+sizeof(PyMemberDef) Py_XDECREF(slots); Py_DECREF(bases); return NULL; } /* Keep name and slots alive in the extended type object */ et = (PyHeapTypeObject *)type; Py_INCREF(name); et->ht_name = name; // 设置类名 et->ht_slots = slots; // 设置设置属性的列表数量 /* Initialize tp_flags */ type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE | Py_TPFLAGS_BASETYPE; if (base->tp_flags & Py_TPFLAGS_HAVE_GC) type->tp_flags |= Py_TPFLAGS_HAVE_GC; /* It's a new-style number unless it specifically inherits any old-style numeric behavior */ if ((base->tp_flags & Py_TPFLAGS_CHECKTYPES) || (base->tp_as_number == NULL)) type->tp_flags |= Py_TPFLAGS_CHECKTYPES; /* Initialize essential fields */ type->tp_as_number = &et->as_number; // 设置PyTypeObject中各个域的值 type->tp_as_sequence = &et->as_sequence; type->tp_as_mapping = &et->as_mapping; type->tp_as_buffer = &et->as_buffer; type->tp_name = PyString_AS_STRING(name); /* Set tp_base and tp_bases */ type->tp_bases = bases; // 设置PyTypeObject的基类列表 Py_INCREF(base); type->tp_base = base; // 设置父类 /* Initialize tp_dict from passed-in dict */ type->tp_dict = dict = PyDict_Copy(dict); // 设置PyTypeObject的tp_dict属性,将传入的属性拷贝到tp_dict中 if (dict == NULL) { Py_DECREF(type); return NULL; } /* Set __module__ in the dict */ if (PyDict_GetItemString(dict, "__module__") == NULL) { // 在属性列表中设置__module__属性值 tmp = PyEval_GetGlobals(); if (tmp != NULL) { tmp = PyDict_GetItemString(tmp, "__name__"); if (tmp != NULL) { if (PyDict_SetItemString(dict, "__module__", tmp) < 0) return NULL; } } } /* Set tp_doc to a copy of dict['__doc__'], if the latter is there and is a string. The __doc__ accessor will first look for tp_doc; if that fails, it will still look into __dict__. */ { PyObject *doc = PyDict_GetItemString(dict, "__doc__"); // 设置属性列表中__doc__的值 if (doc != NULL && PyString_Check(doc)) { const size_t n = (size_t)PyString_GET_SIZE(doc); char *tp_doc = (char *)PyObject_MALLOC(n+1); if (tp_doc == NULL) { Py_DECREF(type); return NULL; } memcpy(tp_doc, PyString_AS_STRING(doc), n+1); type->tp_doc = tp_doc; } } /* Special-case __new__: if it's a plain function, make it a static function */ tmp = PyDict_GetItemString(dict, "__new__"); // 如果属性列表中重写了__new__则将该方法设置成静态方法 if (tmp != NULL && PyFunction_Check(tmp)) { tmp = PyStaticMethod_New(tmp); if (tmp == NULL) { Py_DECREF(type); return NULL; } PyDict_SetItemString(dict, "__new__", tmp); // 景生成的静态方法覆盖属性列表中的值 Py_DECREF(tmp); } /* Add descriptors for custom slots from __slots__, or for __dict__ */ mp = PyHeapType_GET_MEMBERS(et); // 获取机制的偏移量 slotoffset = base->tp_basicsize; // 获取机制偏移量 if (slots != NULL) { // 如果slots不为空 for (i = 0; i < nslots; i++, mp++) { // 如果有值,则添加 mp->name = PyString_AS_STRING( PyTuple_GET_ITEM(slots, i)); mp->type = T_OBJECT_EX; mp->offset = slotoffset; if (base->tp_weaklistoffset == 0 && strcmp(mp->name, "__weakref__") == 0) { add_weak++; mp->type = T_OBJECT; mp->flags = READONLY; type->tp_weaklistoffset = slotoffset; } slotoffset += sizeof(PyObject *); } } if (add_dict) { // 如果add_dict有值 if (base->tp_itemsize) type->tp_dictoffset = -(long)sizeof(PyObject *); else type->tp_dictoffset = slotoffset; slotoffset += sizeof(PyObject *); // 添加一个字节偏移量 } if (add_weak) { assert(!base->tp_itemsize); type->tp_weaklistoffset = slotoffset; slotoffset += sizeof(PyObject *); // 添加一个字节偏移量 } type->tp_basicsize = slotoffset; // 偏移的总大小 type->tp_itemsize = base->tp_itemsize; type->tp_members = PyHeapType_GET_MEMBERS(et); // 设置相关域属性 if (type->tp_weaklistoffset && type->tp_dictoffset) type->tp_getset = subtype_getsets_full; else if (type->tp_weaklistoffset && !type->tp_dictoffset) type->tp_getset = subtype_getsets_weakref_only; else if (!type->tp_weaklistoffset && type->tp_dictoffset) type->tp_getset = subtype_getsets_dict_only; else type->tp_getset = NULL; /* Special case some slots */ if (type->tp_dictoffset != 0 || nslots > 0) { if (base->tp_getattr == NULL && base->tp_getattro == NULL) type->tp_getattro = PyObject_GenericGetAttr; if (base->tp_setattr == NULL && base->tp_setattro == NULL) type->tp_setattro = PyObject_GenericSetAttr; } type->tp_dealloc = subtype_dealloc; /* Enable GC unless there are really no instance variables possible */ if (!(type->tp_basicsize == sizeof(PyObject) && type->tp_itemsize == 0)) type->tp_flags |= Py_TPFLAGS_HAVE_GC; /* Always override allocation strategy to use regular heap */ type->tp_alloc = PyType_GenericAlloc; if (type->tp_flags & Py_TPFLAGS_HAVE_GC) {
type->tp_free = PyObject_GC_Del; type->tp_traverse = subtype_traverse; type->tp_clear = subtype_clear; } else type->tp_free = PyObject_Del; /* Initialize the rest */ if (PyType_Ready(type) < 0) { // 初始化该type的类型 Py_DECREF(type); return NULL; } /* Put the proper slots in place */ fixup_slot_dispatchers(type); // 更新相应的描述符 return (PyObject *)type; // 返回生成的对象}

type_new的工作相对复杂,主要工作是确定元类,确定最佳基类,然后申请内存,然后设置相关域的值,设置完成后初始化该类,然后更新相应的描述符,然后返回初始化后的type。在本例中,由于元类是type,最佳父类是Object所以对于的申请内存,调用初始化都是通过调用相应方法来实现的,此时自定义类初始化完成。

此时我们继续查看脚本文件的字节码的执行流程,此时执行到了;

15          22 LOAD_NAME                1 (A)             25 CALL_FUNCTION            0             28 STORE_NAME               2 (a)

此时初始化了类A到一个实例a,先获取刚刚创建的类A,然后调用CALL_FUNCTION在该函数执行流程,由于传入的A不是函数对象而是类,所有会执行do_call;

static PyObject *call_function(PyObject ***pp_stack, int oparg#ifdef WITH_TSC        , uint64* pintr0, uint64* pintr1#endif        ){        ...        if (PyFunction_Check(func))                                     // 检查是否是函数类型            x = fast_function(func, pp_stack, n, na, nk);               // 处理快速方法        else            x = do_call(func, pp_stack, na, nk);        ...}

我们查看do_call方法,执行了哪些操作;

static PyObject *do_call(PyObject *func, PyObject ***pp_stack, int na, int nk){    ...   // 参数,传入函数的检查,将参数进行转换    result = PyObject_Call(func, callargs, kwdict); call_fail:    Py_XDECREF(callargs);    Py_XDECREF(kwdict);    return result;}

此时调用了PyObject_Call,调用了跟在创建类时使用的方法。此时将会调用class A的type的tp_new函数,而此时class A的type任然是PyType_Type,所以还是调用该类型的tp_new,但是在初始化class A时会让A继承基类的操作,当时A初始化时没有定义tp_new操作,所以会继承object的tp_new,此时,在调用class A的tp_new时,将会调用object的tp_new指向的方法-object_new;

static PyObject *object_new(PyTypeObject *type, PyObject *args, PyObject *kwds){    if (type->tp_init == object_init && (PyTuple_GET_SIZE(args) ||         (kwds && PyDict_Check(kwds) && PyDict_Size(kwds)))) {        PyErr_SetString(PyExc_TypeError,                "default __new__ takes no parameters");        return NULL;    }    return type->tp_alloc(type, 0);    // 调用PyBaseObject_Type的PyType_GenericAlloc}

其中调用PyType_GenericAlloc,会将生成实例的ob-type改为该类

PyObject *PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems){    PyObject *obj;    const size_t size = _PyObject_VAR_SIZE(type, nitems+1);    /* note that we need to add one, for the sentinel */    if (PyType_IS_GC(type))        obj = _PyObject_GC_Malloc(size);    else        obj = (PyObject *)PyObject_MALLOC(size);    if (obj == NULL)        return PyErr_NoMemory();    memset(obj, '\0', size);    if (type->tp_flags & Py_TPFLAGS_HEAPTYPE)        Py_INCREF(type);    if (type->tp_itemsize == 0)        PyObject_INIT(obj, type);    else        (void) PyObject_INIT_VAR((PyVarObject *)obj, type, nitems);    if (PyType_IS_GC(type))        _PyObject_GC_TRACK(obj);    return obj;}// 将生成的类型的ob_type更改为生成的类#define PyObject_INIT(op, typeobj) \    ( (op)->ob_type = (typeobj), _Py_NewReference((PyObject *)(op)), (op) )#define PyObject_INIT_VAR(op, typeobj, size) \    ( (op)->ob_size = (size), PyObject_INIT((op), (typeobj))

调用了PyType_GenericAlloc申请内存,然后就完成了类的初始化完成。在申请内存完成后,返回type_new后将判断执行的是否是生成实例,如果是实例并且有init方法,则调用init方法进行初始化工作。但是在本例中,因为自定义了init方法,所有会在fixup_slot_dispatchers来更新用户自定义的方法,此时的init方法就会被替换成slot_tp_init,该方法就是调用自身重写的init方法来进行初始化。初始化完成后就生成了a的实例。

继续查看脚本文件中的字节码;

16          31 LOAD_NAME                2 (a)             34 LOAD_ATTR                3 (f)             37 CALL_FUNCTION            0             40 POP_TOP

此时就是调用a.f()来访问实例上的属性,根据执行的字节码可知,先加载实例a,然后调用LOAD_ATTR,此时我们查看该字节码执行的流程;

case LOAD_ATTR:            w = GETITEM(names, oparg);            v = TOP();            x = PyObject_GetAttr(v, w);            Py_DECREF(v);            SET_TOP(x);            if (x != NULL) continue;

先获取names中的f,然后在将实例a和f作为参数,传入PyObject_GetAttr中去获取属性;

PyObject *PyObject_GetAttr(PyObject *v, PyObject *name){    PyTypeObject *tp = v->ob_type;                       // 传入实例的类型,此时就是生成类A的类型    if (!PyString_Check(name)) {                         // 检查传入的属性是否是字符串#ifdef Py_USING_UNICODE        /* The Unicode to string conversion is done here because the           existing tp_getattro slots expect a string object as name           and we wouldn't want to break those. */        if (PyUnicode_Check(name)) {            name = _PyUnicode_AsDefaultEncodedString(name, NULL);            if (name == NULL)                return NULL;        }        else#endif        {            PyErr_Format(PyExc_TypeError,                     "attribute name must be string, not '%.200s'",                     name->ob_type->tp_name);            return NULL;        }    }    if (tp->tp_getattro != NULL)                        // 如果tp_getattro不为空,先调用该方法查找        return (*tp->tp_getattro)(v, name);    if (tp->tp_getattr != NULL)                         // 如果tp_getattro为空,则调用tp_getattr查找        return (*tp->tp_getattr)(v, PyString_AS_STRING(name));    PyErr_Format(PyExc_AttributeError,             "'%.50s' object has no attribute '%.400s'",             tp->tp_name, PyString_AS_STRING(name));    return NULL;}

由于类A继承了object所以tp->tp_getattro就是调用了object中的PyObject_GenericGetAttr,对应代码如下;

PyObject *PyObject_GenericGetAttr(PyObject *obj, PyObject *name){    PyTypeObject *tp = obj->ob_type;                // 获取类的类型,此时传入为A    PyObject *descr = NULL;    PyObject *res = NULL;    descrgetfunc f;    Py_ssize_t dictoffset;                          // 相对于tp的__dict__的偏移量    PyObject **dictptr;    if (!PyString_Check(name)){                     // 检查传入的名称是否为字符串#ifdef Py_USING_UNICODE        /* The Unicode to string conversion is done here because the           existing tp_setattro slots expect a string object as name           and we wouldn't want to break those. */        if (PyUnicode_Check(name)) {            name = PyUnicode_AsEncodedString(name, NULL, NULL);            if (name == NULL)                return NULL;        }        else#endif        {            PyErr_Format(PyExc_TypeError,                     "attribute name must be string, not '%.200s'",                     name->ob_type->tp_name);            return NULL;        }    }    else        Py_INCREF(name);    if (tp->tp_dict == NULL) {                      // 如果类型为空,则先初始化该类型        if (PyType_Ready(tp) < 0)            goto done;    }    /* Inline _PyType_Lookup */                     // 先从父类的tp_dict中查找f对应的descr    {        Py_ssize_t i, n;        PyObject *mro, *base, *dict;        /* Look in tp_dict of types in MRO */        mro = tp->tp_mro;        assert(mro != NULL);        assert(PyTuple_Check(mro));        n = PyTuple_GET_SIZE(mro);                          // 获取mro的大小        for (i = 0; i < n; i++) {                           // 依次偏移mro列表            base = PyTuple_GET_ITEM(mro, i);                // 获取对应的基类            if (PyClass_Check(base))                dict = ((PyClassObject *)base)->cl_dict;            else {                assert(PyType_Check(base));                dict = ((PyTypeObject *)base)->tp_dict;            }            assert(dict && PyDict_Check(dict));             // 从mro类的中的dict中查找是否有name 存在            descr = PyDict_GetItem(dict, name);            if (descr != NULL)                break;        }    }    Py_XINCREF(descr);    f = NULL;    if (descr != NULL &&        PyType_HasFeature(descr->ob_type, Py_TPFLAGS_HAVE_CLASS)) {        f = descr->ob_type->tp_descr_get;                   // 如果在mro中查找到了,则调用对应类型的__get__方法        if (f != NULL && PyDescr_IsData(descr)) {           // 如果descr中,对应的__set__方法不为空            res = f(descr, obj, (PyObject *)obj->ob_type);  // 执行该方法并返回结果值            Py_DECREF(descr);            goto done;        }    }    /* Inline _PyObject_GetDictPtr */    dictoffset = tp->tp_dictoffset;                         // 获取该类的__dict__的偏移量    if (dictoffset != 0) {        PyObject *dict;        if (dictoffset < 0) {            Py_ssize_t tsize;            size_t size;            tsize = ((PyVarObject *)obj)->ob_size;            if (tsize < 0)                tsize = -tsize;            size = _PyObject_VAR_SIZE(tp, tsize);            dictoffset += (long)size;            assert(dictoffset > 0);            assert(dictoffset % SIZEOF_VOID_P == 0);        }        dictptr = (PyObject **) ((char *)obj + dictoffset);   // 获取对应的__dict__对应的地址        dict = *dictptr;                                      // 获取实例的字典        if (dict != NULL) {            res = PyDict_GetItem(dict, name);                 // 在实例字典中,查找name,获取对应的方法            if (res != NULL) {                Py_INCREF(res);                Py_XDECREF(descr);                goto done;            }        }    }    if (f != NULL) {                                          // 如果有__get__ ,在实例中的__dict__中,没有找到,并且__set__为空        res = f(descr, obj, (PyObject *)obj->ob_type);             Py_DECREF(descr);                                     // 则调用对应的__get__        goto done;    }    if (descr != NULL) {                                      // 如果以上都没有,但是在基类中找到了对应的descr,则直接调用该函数        res = descr;                                                /* descr was already increfed above */        goto done;    }    PyErr_Format(PyExc_AttributeError,             "'%.50s' object has no attribute '%.400s'",             tp->tp_name, PyString_AS_STRING(name));  done:    Py_DECREF(name);    return res;}

这个属性的查找机制,相对有点复杂,首先;

查找的descr大致分为两种,
1,data descriptor,定义了getset的两种方法;
2,not data descriptor,定义了get的方法
选择流程如下;
1,如果在父类中查找到了data descriptor对应的属性,则直接调用该属性;
2,如果第一步没找到,则再查找实例属性对应的属性,如果没找到则查找类对应的属性。
此时在本例中,A.mro为(A,object),此时再mro的查找中会找到对应的函数f,然后会获取对应的descr,此时会在f = descr->ob_type->tp_descr_get;将PyFunctionObject的ob_type的tp_descr_get方法赋值给f;当继续执行时,由于不符合data descriptor所以会执行到;

if (f != NULL) {                                          // 如果有__get__ ,在实例中的__dict__中,没有找到,并且__set__为空        res = f(descr, obj, (PyObject *)obj->ob_type);             Py_DECREF(descr);                                     // 则调用对应的__get__        goto done;    }   if (f != NULL) {                                          // 如果有__get__ ,在实例中的__dict__中,没有找到,并且__set__为空        res = f(descr, obj, (PyObject *)obj->ob_type);             Py_DECREF(descr);                                     // 则调用对应的__get__        goto done;    }

所以g中的self操作,都隐藏在PyFunctionType的ob_type的tp_descr_get中,此时我们查看;

static PyObject *func_descr_get(PyObject *func, PyObject *obj, PyObject *type){    if (obj == Py_None)        obj = NULL;    return PyMethod_New(func, obj, type);}

调用了PyMethod_New方法;

PyObject *PyMethod_New(PyObject *func, PyObject *self, PyObject *klass){    register PyMethodObject *im;    if (!PyCallable_Check(func)) {        PyErr_BadInternalCall();        return NULL;    }    im = free_list;                                         // 缓存池概念    if (im != NULL) {        free_list = (PyMethodObject *)(im->im_self);        PyObject_INIT(im, &PyMethod_Type);    }    else {        im = PyObject_GC_New(PyMethodObject, &PyMethod_Type);        if (im == NULL)            return NULL;    }    im->im_weakreflist = NULL;    Py_INCREF(func);    im->im_func = func;                         // 设置要执行的函数    Py_XINCREF(self);    im->im_self = self;                         // 设置类的self    Py_XINCREF(klass);     im->im_class = klass;                       // 设置类的值    _PyObject_GC_TRACK(im);    return (PyObject *)im;}

此时可以看见传入的实例obj,作为了self,存入了im-im_self中,然后将PyMethodObject作为参数返回。此时,再调用CALL_FUNCTION时,

static PyObject *call_function(PyObject ***pp_stack, int oparg#ifdef WITH_TSC        , uint64* pintr0, uint64* pintr1#endif        ){    int na = oparg & 0xff;         // 获取输入参数的个数    int nk = (oparg>>8) & 0xff;    // 获取位置参数的个数    int n = na + 2 * nk;           //  总大小,由于一个位置参数由key和value组成,所有乘2    PyObject **pfunc = (*pp_stack) - n - 1;      // 获取函数对象    PyObject *func = *pfunc;    PyObject *x, *w;    /* Always dispatch PyCFunction first, because these are       presumed to be the most frequent callable object.    */    if (PyCFunction_Check(func) && nk == 0) {           // 检查func的类型,是否为cfunc        ...    } else {        if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) {  // 检查func是否是类访问的方法            /* optimize access to bound methods */            PyObject *self = PyMethod_GET_SELF(func);                   // 获取对应的实例            PCALL(PCALL_METHOD);            PCALL(PCALL_BOUND_METHOD);            Py_INCREF(self);            func = PyMethod_GET_FUNCTION(func);                         // 获取对应的func            Py_INCREF(func);            Py_DECREF(*pfunc);            *pfunc = self;                                              // 将pfunc设置成实例自己,相当于将实例压栈            na++;                                                       // 位置参数加1            n++;                                                        // 参数总数加1        } else            Py_INCREF(func);        READ_TIMESTAMP(*pintr0);        if (PyFunction_Check(func))                                     // 检查是否是函数类型            x = fast_function(func, pp_stack, n, na, nk);               // 处理快速方法        else            x = do_call(func, pp_stack, na, nk);        READ_TIMESTAMP(*pintr1);        Py_DECREF(func);    }    ...}

此时,相当于默认添加了一个默认的位置参数,然后快速调用fast_function此时的执行流程与分析过的函数调用时一样的了,至此无参函数的调用就分析完成。解析来分析有参函数的执行流程。

继续执教脚本字节码;

17          41 LOAD_NAME                2 (a)             44 LOAD_ATTR                4 (g)             47 LOAD_CONST               2 (5)             50 CALL_FUNCTION            1             53 POP_TOP                          54 LOAD_CONST               3 (None)             57 RETURN_VALUE

此时就是调用了a.g(5),此时先加载了实例,然后调用LOAD_ATTR g,接着LOAD_CONST 函数参数5,此时调用了CALL_FUNCTION,此时根据无参函数的分析可知,此时就压栈了一个常量5作为函数的位置参数,接下来的流程就与有参函数的调用流程是一样的,主要分析一下对应的g的字节码;

8           0 LOAD_FAST                1 (aValue)              3 LOAD_FAST                0 (self)              6 STORE_ATTR               0 (aValue)  9           9 LOAD_FAST                0 (self)             12 LOAD_ATTR                0 (aValue)             15 PRINT_ITEM                       16 PRINT_NEWLINE        10          17 LOAD_CONST               1 ('A.g')             20 PRINT_ITEM                       21 PRINT_NEWLINE                    22 LOAD_CONST               0 (None)             25 RETURN_VALUE

主要在于先LOAD_FAST,获取了输入的参数值,然后加载了self,然后调用了STORE_ATTR;

case STORE_ATTR:            w = GETITEM(names, oparg);    // 获取对应names中的值            v = TOP();                   // 对应压入的self            u = SECOND();                // 对应压入的aValue            STACKADJ(-2);            err = PyObject_SetAttr(v, w, u); /* v.w = u */            Py_DECREF(v);            Py_DECREF(u);            if (err == 0) continue;            break;

此时,查看PyObject_SetAttr;

intPyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value){    PyTypeObject *tp = v->ob_type;                                      // 获取类A    int err;    if (!PyString_Check(name)){                                         // 检查传入名称是否是字符串#ifdef Py_USING_UNICODE        /* The Unicode to string conversion is done here because the           existing tp_setattro slots expect a string object as name           and we wouldn't want to break those. */        if (PyUnicode_Check(name)) {            name = PyUnicode_AsEncodedString(name, NULL, NULL);            if (name == NULL)                return -1;        }        else#endif        {            PyErr_Format(PyExc_TypeError,                     "attribute name must be string, not '%.200s'",                     name->ob_type->tp_name);            return -1;        }    }    else        Py_INCREF(name);    PyString_InternInPlace(&name);    if (tp->tp_setattro != NULL) {        err = (*tp->tp_setattro)(v, name, value);                       // 调用类A的tp_setattro方法,由于此时对应于object中的tp_setattro        Py_DECREF(name);        return err;    }    if (tp->tp_setattr != NULL) {                                       // 如果tp_setattro为空,则调用tp_setattr设置属性        err = (*tp->tp_setattr)(v, PyString_AS_STRING(name), value);        Py_DECREF(name);        return err;    }    Py_DECREF(name);    if (tp->tp_getattr == NULL && tp->tp_getattro == NULL)              // 如果类型两个方法都没有则报错        PyErr_Format(PyExc_TypeError,                 "'%.100s' object has no attributes "                 "(%s .%.100s)",                 tp->tp_name,                 value==NULL ? "del" : "assign to",                 PyString_AS_STRING(name));    else        PyErr_Format(PyExc_TypeError,                 "'%.100s' object has only read-only attributes "                 "(%s .%.100s)",                 tp->tp_name,                 value==NULL ? "del" : "assign to",                 PyString_AS_STRING(name));    return -1;}

此时,由于类A继承了object的tp_setattro,所以调用了PyObject_GenericSetAttr;

intPyObject_GenericSetAttr(PyObject *obj, PyObject *name, PyObject *value){    PyTypeObject *tp = obj->ob_type;    PyObject *descr;    descrsetfunc f;    PyObject **dictptr;    int res = -1;    if (!PyString_Check(name)){#ifdef Py_USING_UNICODE        /* The Unicode to string conversion is done here because the           existing tp_setattro slots expect a string object as name           and we wouldn't want to break those. */        if (PyUnicode_Check(name)) {            name = PyUnicode_AsEncodedString(name, NULL, NULL);            if (name == NULL)                return -1;        }        else#endif        {            PyErr_Format(PyExc_TypeError,                     "attribute name must be string, not '%.200s'",                     name->ob_type->tp_name);            return -1;        }    }    else        Py_INCREF(name);    if (tp->tp_dict == NULL) {                          // 如果属性为空则先初始化        if (PyType_Ready(tp) < 0)            goto done;    }    descr = _PyType_Lookup(tp, name);                   // 查找mro中是否有该属性    f = NULL;    if (descr != NULL &&        PyType_HasFeature(descr->ob_type, Py_TPFLAGS_HAVE_CLASS)) {        f = descr->ob_type->tp_descr_set;        if (f != NULL && PyDescr_IsData(descr)) {            res = f(descr, obj, value);                 // 如果有则重置该属性            goto done;        }    }    dictptr = _PyObject_GetDictPtr(obj);                // 获取__dict__相对于实例的位移    if (dictptr != NULL) {        PyObject *dict = *dictptr;        if (dict == NULL && value != NULL) {            // 如果没有则生成一个属性dict            dict = PyDict_New();            if (dict == NULL)                goto done;            *dictptr = dict;                            // 将该字典的位置设置到实例对应的__dict__z中                }         if (dict != NULL) {            if (value == NULL)                res = PyDict_DelItem(dict, name);       // 如果传入的值为空,则删除该属性            else                res = PyDict_SetItem(dict, name, value);            // 如果传入的有值,则设置该属性到__dict__中            if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError))                PyErr_SetObject(PyExc_AttributeError, name);            goto done;        }    }    if (f != NULL) {        res = f(descr, obj, value);         goto done;    }    if (descr == NULL) {        PyErr_Format(PyExc_AttributeError,                 "'%.100s' object has no attribute '%.200s'",                 tp->tp_name, PyString_AS_STRING(name));        goto done;    }    PyErr_Format(PyExc_AttributeError,             "'%.50s' object attribute '%.400s' is read-only",             tp->tp_name, PyString_AS_STRING(name));  done:    Py_DECREF(name);    return res;}

至此,属性的设置流程执行完成。

以上就是有关Python中类的基本操作,如有兴趣可自行查看。

转载地址:https://blog.csdn.net/qq_33339479/article/details/79867609 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Python源码学习:多线程实现机制
下一篇:Python源码学习:Python类机制分析

发表评论

最新留言

感谢大佬
[***.8.128.20]2024年03月28日 12时09分15秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章