当前位置: 首页 > 面试题库 >

如何在Python 3中实现super()?

朱刚捷
2023-03-14
问题内容

我想知道Python 3中的新super是如何实现的。

我做了一个小例子之后,这个问题就浮现在脑海中,我得到了一个奇怪的错误。我正在使用Pyutilib组件体系结构(PCA),并且已经制作了自定义元类来驱动另一个类的创建:

    from pyutilib.component.core import implements, SingletonPlugin, PluginMeta, Interface

    class IPass(Interface):
        pass

    class __MetaPlugin(PluginMeta):
        def __new__(cls, name, baseClasses, classdict):
            print(cls, name, baseClasses, classdict)
            if baseClasses:
                baseClasses += (SingletonPlugin,)
            return PluginMeta.__new__(cls, name, baseClasses, classdict)

    class Pass(metaclass=__MetaPlugin):
        implements(IPass)

        def __init__(self, inputs=[], outputs=[]):
            self.inputs = []
            self.outputs = []


    class A(Pass):
        def __init__(self):
            print(self.__class__) # <class '__main__.A'>
            print(self.__class__.__class__) # <class '__main__.__MetaPlugin'>
            print(PluginMeta.__class__) # <class 'type'>
            super().__init__() # SystemError: super(): empty __class__ cell
            #Pass.__init__(self) - this works

    a = A()

我收到以下错误:

super().__init__() SystemError: super(): empty __class__ cell

我wonderign究竟超(一样),它引发的错误super().__init__(),而所有的self.__class__self.__class__.__class__PluginMeta.__class__存在。另外的“老方法”-Pass.__init__(self)正在工作。


问题答案:

如何super()执行?这是python3.3的代码:

    /* Cooperative 'super' */

    typedef struct {
        PyObject_HEAD
        PyTypeObject *type;
        PyObject *obj;
        PyTypeObject *obj_type;
    } superobject;

    static PyMemberDef super_members[] = {
        {"__thisclass__", T_OBJECT, offsetof(superobject, type), READONLY,
         "the class invoking super()"},
        {"__self__",  T_OBJECT, offsetof(superobject, obj), READONLY,
         "the instance invoking super(); may be None"},
        {"__self_class__", T_OBJECT, offsetof(superobject, obj_type), READONLY,
         "the type of the instance invoking super(); may be None"},
        {0}
    };

    static void
    super_dealloc(PyObject *self)
    {
        superobject *su = (superobject *)self;

        _PyObject_GC_UNTRACK(self);
        Py_XDECREF(su->obj);
        Py_XDECREF(su->type);
        Py_XDECREF(su->obj_type);
        Py_TYPE(self)->tp_free(self);
    }

    static PyObject *
    super_repr(PyObject *self)
    {
        superobject *su = (superobject *)self;

        if (su->obj_type)
            return PyUnicode_FromFormat(
                "<super: <class '%s'>, <%s object>>",
                su->type ? su->type->tp_name : "NULL",
                su->obj_type->tp_name);
        else
            return PyUnicode_FromFormat(
                "<super: <class '%s'>, NULL>",
                su->type ? su->type->tp_name : "NULL");
    }

    static PyObject *
    super_getattro(PyObject *self, PyObject *name)
    {
        superobject *su = (superobject *)self;
        int skip = su->obj_type == NULL;

        if (!skip) {
            /* We want __class__ to return the class of the super object
               (i.e. super, or a subclass), not the class of su->obj. */
            skip = (PyUnicode_Check(name) &&
                PyUnicode_GET_LENGTH(name) == 9 &&
                PyUnicode_CompareWithASCIIString(name, "__class__") == 0);
        }

        if (!skip) {
            PyObject *mro, *res, *tmp, *dict;
            PyTypeObject *starttype;
            descrgetfunc f;
            Py_ssize_t i, n;

            starttype = su->obj_type;
            mro = starttype->tp_mro;

            if (mro == NULL)
                n = 0;
            else {
                assert(PyTuple_Check(mro));
                n = PyTuple_GET_SIZE(mro);
            }
            for (i = 0; i < n; i++) {
                if ((PyObject *)(su->type) == PyTuple_GET_ITEM(mro, i))
                    break;
            }
            i++;
            res = NULL;
            /* keep a strong reference to mro because starttype->tp_mro can be
               replaced during PyDict_GetItem(dict, name)  */
            Py_INCREF(mro);
            for (; i < n; i++) {
                tmp = PyTuple_GET_ITEM(mro, i);
                if (PyType_Check(tmp))
                    dict = ((PyTypeObject *)tmp)->tp_dict;
                else
                    continue;
                res = PyDict_GetItem(dict, name);
                if (res != NULL) {
                    Py_INCREF(res);
                    f = Py_TYPE(res)->tp_descr_get;
                    if (f != NULL) {
                        tmp = f(res,
                            /* Only pass 'obj' param if
                               this is instance-mode super
                               (See SF ID #743627)
                            */
                            (su->obj == (PyObject *)
                                        su->obj_type
                                ? (PyObject *)NULL
                                : su->obj),
                            (PyObject *)starttype);
                        Py_DECREF(res);
                        res = tmp;
                    }
                    Py_DECREF(mro);
                    return res;
                }
            }
            Py_DECREF(mro);
        }
        return PyObject_GenericGetAttr(self, name);
    }

    static PyTypeObject *
    supercheck(PyTypeObject *type, PyObject *obj)
    {
        /* Check that a super() call makes sense.  Return a type object.

           obj can be a class, or an instance of one:

           - If it is a class, it must be a subclass of 'type'.      This case is
             used for class methods; the return value is obj.

           - If it is an instance, it must be an instance of 'type'.  This is
             the normal case; the return value is obj.__class__.

           But... when obj is an instance, we want to allow for the case where
           Py_TYPE(obj) is not a subclass of type, but obj.__class__ is!
           This will allow using super() with a proxy for obj.
        */

        /* Check for first bullet above (special case) */
        if (PyType_Check(obj) && PyType_IsSubtype((PyTypeObject *)obj, type)) {
            Py_INCREF(obj);
            return (PyTypeObject *)obj;
        }

        /* Normal case */
        if (PyType_IsSubtype(Py_TYPE(obj), type)) {
            Py_INCREF(Py_TYPE(obj));
            return Py_TYPE(obj);
        }
        else {
            /* Try the slow way */
            PyObject *class_attr;

            class_attr = _PyObject_GetAttrId(obj, &PyId___class__);
            if (class_attr != NULL &&
                PyType_Check(class_attr) &&
                (PyTypeObject *)class_attr != Py_TYPE(obj))
            {
                int ok = PyType_IsSubtype(
                    (PyTypeObject *)class_attr, type);
                if (ok)
                    return (PyTypeObject *)class_attr;
            }

            if (class_attr == NULL)
                PyErr_Clear();
            else
                Py_DECREF(class_attr);
        }

        PyErr_SetString(PyExc_TypeError,
                        "super(type, obj): "
                        "obj must be an instance or subtype of type");
        return NULL;
    }

    static PyObject *
    super_descr_get(PyObject *self, PyObject *obj, PyObject *type)
    {
        superobject *su = (superobject *)self;
        superobject *newobj;

        if (obj == NULL || obj == Py_None || su->obj != NULL) {
            /* Not binding to an object, or already bound */
            Py_INCREF(self);
            return self;
        }
        if (Py_TYPE(su) != &PySuper_Type)
            /* If su is an instance of a (strict) subclass of super,
               call its type */
            return PyObject_CallFunctionObjArgs((PyObject *)Py_TYPE(su),
                                                su->type, obj, NULL);
        else {
            /* Inline the common case */
            PyTypeObject *obj_type = supercheck(su->type, obj);
            if (obj_type == NULL)
                return NULL;
            newobj = (superobject *)PySuper_Type.tp_new(&PySuper_Type,
                                                     NULL, NULL);
            if (newobj == NULL)
                return NULL;
            Py_INCREF(su->type);
            Py_INCREF(obj);
            newobj->type = su->type;
            newobj->obj = obj;
            newobj->obj_type = obj_type;
            return (PyObject *)newobj;
        }
    }

    static int
    super_init(PyObject *self, PyObject *args, PyObject *kwds)
    {
        superobject *su = (superobject *)self;
        PyTypeObject *type = NULL;
        PyObject *obj = NULL;
        PyTypeObject *obj_type = NULL;

        if (!_PyArg_NoKeywords("super", kwds))
            return -1;
        if (!PyArg_ParseTuple(args, "|O!O:super", &PyType_Type, &type, &obj))
            return -1;

        if (type == NULL) {
            /* Call super(), without args -- fill in from __class__
               and first local variable on the stack. */
            PyFrameObject *f = PyThreadState_GET()->frame;
            PyCodeObject *co = f->f_code;
            Py_ssize_t i, n;
            if (co == NULL) {
                PyErr_SetString(PyExc_SystemError,
                                "super(): no code object");
                return -1;
            }
            if (co->co_argcount == 0) {
                PyErr_SetString(PyExc_SystemError,
                                "super(): no arguments");
                return -1;
            }
            obj = f->f_localsplus[0];
            if (obj == NULL) {
                PyErr_SetString(PyExc_SystemError,
                                "super(): arg[0] deleted");
                return -1;
            }
            if (co->co_freevars == NULL)
                n = 0;
            else {
                assert(PyTuple_Check(co->co_freevars));
                n = PyTuple_GET_SIZE(co->co_freevars);
            }
            for (i = 0; i < n; i++) {
                PyObject *name = PyTuple_GET_ITEM(co->co_freevars, i);
                assert(PyUnicode_Check(name));
                if (!PyUnicode_CompareWithASCIIString(name,
                                                      "__class__")) {
                    Py_ssize_t index = co->co_nlocals +
                        PyTuple_GET_SIZE(co->co_cellvars) + i;
                    PyObject *cell = f->f_localsplus[index];
                    if (cell == NULL || !PyCell_Check(cell)) {
                        PyErr_SetString(PyExc_SystemError,
                          "super(): bad __class__ cell");
                        return -1;
                    }
                    type = (PyTypeObject *) PyCell_GET(cell);
                    if (type == NULL) {
                        PyErr_SetString(PyExc_SystemError,
                          "super(): empty __class__ cell");
                        return -1;
                    }
                    if (!PyType_Check(type)) {
                        PyErr_Format(PyExc_SystemError,
                          "super(): __class__ is not a type (%s)",
                          Py_TYPE(type)->tp_name);
                        return -1;
                    }
                    break;
                }
            }
            if (type == NULL) {
                PyErr_SetString(PyExc_SystemError,
                                "super(): __class__ cell not found");
                return -1;
            }
        }

        if (obj == Py_None)
            obj = NULL;
        if (obj != NULL) {
            obj_type = supercheck(type, obj);
            if (obj_type == NULL)
                return -1;
            Py_INCREF(obj);
        }
        Py_INCREF(type);
        su->type = type;
        su->obj = obj;
        su->obj_type = obj_type;
        return 0;
    }

    PyDoc_STRVAR(super_doc,
    "super() -> same as super(__class__, <first argument>)\n"
    "super(type) -> unbound super object\n"
    "super(type, obj) -> bound super object; requires isinstance(obj, type)\n"
    "super(type, type2) -> bound super object; requires issubclass(type2, type)\n"
    "Typical use to call a cooperative superclass method:\n"
    "class C(B):\n"
    "    def meth(self, arg):\n"
    "        super().meth(arg)\n"
    "This works for class methods too:\n"
    "class C(B):\n"
    "    @classmethod\n"
    "    def cmeth(cls, arg):\n"
    "        super().cmeth(arg)\n");

    static int
    super_traverse(PyObject *self, visitproc visit, void *arg)
    {
        superobject *su = (superobject *)self;

        Py_VISIT(su->obj);
        Py_VISIT(su->type);
        Py_VISIT(su->obj_type);

        return 0;
    }

    PyTypeObject PySuper_Type = {
        PyVarObject_HEAD_INIT(&PyType_Type, 0)
        "super",                                    /* tp_name */
        sizeof(superobject),                        /* tp_basicsize */
        0,                                          /* tp_itemsize */
        /* methods */
        super_dealloc,                              /* tp_dealloc */
        0,                                          /* tp_print */
        0,                                          /* tp_getattr */
        0,                                          /* tp_setattr */
        0,                                          /* tp_reserved */
        super_repr,                                 /* tp_repr */
        0,                                          /* tp_as_number */
        0,                                          /* tp_as_sequence */
        0,                                          /* tp_as_mapping */
        0,                                          /* tp_hash */
        0,                                          /* tp_call */
        0,                                          /* tp_str */
        super_getattro,                             /* tp_getattro */
        0,                                          /* tp_setattro */
        0,                                          /* tp_as_buffer */
        Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
            Py_TPFLAGS_BASETYPE,                    /* tp_flags */
        super_doc,                                  /* tp_doc */
        super_traverse,                             /* tp_traverse */
        0,                                          /* tp_clear */
        0,                                          /* tp_richcompare */
        0,                                          /* tp_weaklistoffset */
        0,                                          /* tp_iter */
        0,                                          /* tp_iternext */
        0,                                          /* tp_methods */
        super_members,                              /* tp_members */
        0,                                          /* tp_getset */
        0,                                          /* tp_base */
        0,                                          /* tp_dict */
        super_descr_get,                            /* tp_descr_get */
        0,                                          /* tp_descr_set */
        0,                                          /* tp_dictoffset */
        super_init,                                 /* tp_init */
        PyType_GenericAlloc,                        /* tp_alloc */
        PyType_GenericNew,                          /* tp_new */
        PyObject_GC_Del,                            /* tp_free */
    };

您可以在某处看到super_init检查type == NULL,然后它引发您看到的错误。NULL周围不正常,因此其中可能存在一个错误super(并且请注意,super先前版本中已经存在错误)。至少我认为SystemError应该仅在解释器或其他C代码的某些“内部”失败而不是python代码引起的情况下触发引发这种情况的情况。

另外,这不仅发生在您身上,您还可以在其中找到将此行为视为错误的帖子。



 类似资料:
  • 本文向大家介绍如何用Python3实现Dictionary,包括了如何用Python3实现Dictionary的使用技巧和注意事项,需要的朋友参考一下 python中的字典是一种数据结构,可将键映射到作为键值对的值。它们是经常使用的数据结构之一,并具有许多有趣的属性。通过将它们括在一对大括号中来呈现它们,如下所示。 字典中的元素或键值对用单引号表示,并用冒号分隔。 创建字典 我们通过分配以键形式编

  • 本文向大家介绍Python2及Python3如何实现兼容切换,包括了Python2及Python3如何实现兼容切换的使用技巧和注意事项,需要的朋友参考一下 由于历史原因,Python有两个大的版本分支,Python2和Python3,又由于一些库只支持某个版本分支,所以需要在电脑上同时安装Python2和Python3,因此如何让两个版本的Python兼容,如何让脚本在对应的Python版本上运行

  • 我自己写了一个多进程下载网络大文件的程序:基本思路是多进程分块下载完后,再拼接文件。 这个url的大文件被分割成20个部分,开启了4个进程下载。 测试运行: python3 mpdown.py #速度比单进程快50%以上。 现在我有点不满意这个多进程下载,拼接文件(merge)是在全部下载完成后,再拼接,我想这个拼接动作也修改成多进程并行的,边下载边拼接。 由于下载是多进程的,各进程存在一定的竞争

  • 我正在使用Python 3.2。1并且我无法导入模块。我使用可以工作,但我不能将它与的一起使用,如下所示: 我得到以下错误: 当我写时,它说

  • 问题内容: 当用户访问列表中可见的项目时,如何在底部显示进度栏。 我编写了一个代码,其中使用Web服务获取数据,现在我想填充部分记录,因为我的JSON中大约有 630条记录 。 这是我用来从JSON获取数据并填充到RecyclerView中的整个代码。 这是我的JSON的外观,但是真实的JSON包含600多个记录: 有人可以指导我在我的代码中进行更改的地方吗? 每当用户使用进度条滚动到底部时,我想

  • 问题内容: 我正在使用iOS的Google Maps API,并想使用标记聚类实用程序。我想出了如何显示聚簇标记,但是我想自定义标记。有人可以解释如何设置/更改每个标记或群集标记的图标和标题吗?示例代码将非常有帮助。 到目前为止,这就是我所拥有的。我不知道该如何处理renderClusters和更新函数。 问题答案: 在 Swift 4上 ,我找到了一种针对聚簇标记的干净解决方案,可以为聚簇使用自