¿Cómo se implementa super () en Python 3?

Me pregunto cómo se implementa el nuevo super en Python 3.

Esta pregunta nació en mi cabeza después de que hice un pequeño ejemplo y obtuve un extraño error. Estoy usando la architecture de componentes Pyutilib (PCA) y he creado mi metaclase personalizada para impulsar la creación de otra clase:

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__) #  print(self.__class__.__class__) #  print(PluginMeta.__class__) #  super().__init__() # SystemError: super(): empty __class__ cell #Pass.__init__(self) - this works a = A() 

Estoy recibiendo el siguiente error:

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

Me pregunto qué hace exactamente super (), que genera un error en super().__init__() , mientras que todos los self.__class__ , self.__class__.__class__ y PluginMeta.__class__ existen. Además, la “forma antigua”: el Pass.__init__(self) está funcionando.

¿Cómo se implementa super() ? Aquí está el código para 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( ", <%s object>>", su->type ? su->type->tp_name : "NULL", su->obj_type->tp_name); else return PyUnicode_FromFormat( ", 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 (ie 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__, )\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 */ }; 

Puedes ver en el super_init en algún punto, está el type == NULL verificación type == NULL y luego genera el error que ves. No es normal tener NULL s alrededor, por lo que probablemente haya un error en algún lugar en super (y tenga en cuenta que super ya tenía errores en versiones anteriores). Al menos pensé que los casos en los que se SystemError deberían SystemError solo debido a una falla “interna” del intérprete o algún otro código C y no del código python.

Además, esto no le sucedió solo a usted, puede encontrar una publicación en la que este comportamiento se considera un error.

TL; DR : Este error de "empty __class__ cell" ocurrirá cuando la metaclase intente llamar a un método en la clase definida (o instanciarla) antes de que se haga con sus __new__ y __init__ , y el método llamado usa super . El error también ocurrirá si uno escribe una llamada a super() en una función definida fuera del cuerpo de una clase, e intenta agregar este método a una clase existente y usarlo. ( actualización : este comportamiento se ha corregido en Python 3.6)

Python 3 super hace una referencia implícita a un nombre “mágico” __class__ [*] que se comporta como una variable de celda en el espacio de nombres de cada método de clase.

Esta variable se crea automáticamente al final del mecanismo de creación de clases, es decir, siempre que haya un cuerpo de clase en Python, los __new__ y __init__ la metaclase se ejecuten – cuando __init__ termina, la celda __class__ se llena y se pone a disposición de los métodos de la clase.

Lo que está sucediendo aquí es que es probable (no he visto todo el código) en el código de inicialización de __init__ se llama el __init__ la clase, antes del final de la metaclase __init__ , ya que uno de los puntos de esta metaclase es el manejo de singletons – lo que es probable que suceda es que el mecanismo de metaclase __instance__ instancia de la instancia única y __instance__ antes de regresar de la metaclase ‘ __init__ . El __class__ implícito usado por super no existe en este punto.

Por lo tanto, hacer referencia a la superclase por el nombre codificado, como se tenía que hacer antes de super en Python2, funcionará, y es la mejor manera de lograr lo que desea.

* – Este no es el atributo self.__class__ de la instancia, es una variable __class__ realmente disponible dentro de los métodos:

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

Y ejecutando esto tenemos:

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

Desde el datamodel de Python:

__class__ es una referencia de cierre implícita creada por el comstackdor si cualquier método en el cuerpo de una clase se refiere a __class__ o super . Esto permite que la forma de argumento cero de super() identifique correctamente la clase que se define en función del scope léxico, mientras que la clase o instancia que se usó para realizar la llamada actual se identifica en función del primer argumento que se pasó al método.

Para más detalles, consulte PEP 3135.