Подтвердить что ты не робот

Как реализована функция super() в Python 3?

Мне интересно, как реализован новый супер в Python 3.

Этот вопрос родился у меня после того, как я сделал небольшой пример, и у меня возникла странная ошибка. Я использую Pyutilib Component Architecture (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

Я прекрасно понимаю, что именно super(), что он вызывает ошибку на super().__init__(), тогда как все self.__class__, self.__class__.__class__ и PluginMeta.__class__ существуют. Дополнительно работает "старый способ" - Pass.__init__(self).

4b9b3361

Ответ 1

Как реализовано 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.

Кроме того, это произошло не только с вами, вы можете найти сообщение , в котором это поведение считается ошибкой.

Ответ 2

TL; DR. Эта ошибка "empty __class__ cell" произойдет, когда метакласс попытается вызвать метод в определенном классе (или создать его экземпляр) до того, как он будет выполнен с помощью __new__ и __init__, а вызываемый метод использует super. Ошибка также произойдет, если вы напишете вызов super() в функции, определенной вне тела класса, и попытаетесь добавить этот метод к существующему классу и использовать его. ( обновление: это поведение было исправлено в Python 3.6)

Python 3 super делает неявную ссылку на "магическое" __class__ [*] имя, которое ведет себя как переменная ячейки в пространстве имен каждого метода класса.

Эта переменная создается автоматически в конце механизма создания класса, т.е. всякий раз, когда в Python есть тело класса, выполняются метаклассы __new__ и __init__ - когда __init__ заканчивается, ячейка __class__ заполняется и становится доступным для методов класса.

Что здесь происходит, это, вероятно, (я не смотрел весь код) в коде инициализации PluginMeta, класс __init__ вызывается до конца метакласса __init__ - так как один точек этого метакласса обрабатывает одиночные точки - то, что может произойти, состоит в том, что механизм метакласса создает экземпляр единственного экземпляра и заполняет __instance__ перед возвратом из метакласса __init__. Неявный __class__, используемый супер, на данный момент не существует.

Таким образом, ссылка на суперкласс с жестко запрограммированным именем, как это делалось до super в Python2, будет работать - и это лучший способ добиться того, что вы там хотите.

* - это не атрибут self.__class__ экземпляра, это переменная __class__, фактически доступная внутри методов:

class A:
   def a(self):
      print ("Instance class: {}, "
             "actual class where this line is coded: {}".format(
                 self.__class__, __class__))

class B(A):
   pass

И запустив это, мы имеем:

>>> B().a()
Instance class: <class '__main__.B'>, actual class where this line is coded: <class '__main__.A'>
>>> 

Из базы данных Python:

__class__ - это неявная ссылка на замыкание, созданная компилятором if любые методы в классе класса относятся к __class__ или super. Эта разрешает форму нулевого аргумента super() правильно идентифицировать класс определяется на основе лексического охвата, тогда как класс или экземпляр, который использовался для того, чтобы текущий вызов был идентифицирован на основе первый аргумент передан методу.

Подробнее см. PEP 3135