Considere el siguiente ejemplo de código
import abc class ABCtest(abc.ABC): @abc.abstractmethod def foo(self): raise RuntimeError("Abstract method was called, this should be impossible") class ABCtest_B(ABCtest): pass test = ABCtest_B()
Esto plantea correctamente el error:
Traceback (most recent call last): File "/.../test.py", line 10, in test = ABCtest_B() TypeError: Can't instantiate abstract class ABCtest_B with abstract methods foo
Sin embargo, cuando la subclase de ABCtest
también se hereda de un tipo incorporado como str
o list
no hay error y test.foo()
llama al método abstracto:
class ABCtest_C(ABCtest, str): pass >>> test = ABCtest_C() >>> test.foo() Traceback (most recent call last): File "", line 1, in test.foo() File "/.../test.py", line 5, in foo raise RuntimeError("Abstract method was called, this should be impossible") RuntimeError: Abstract method was called, this should be impossible
Esto parece suceder cuando se hereda de cualquier clase definida en C, incluyendo itertools.chain
y numpy.ndarray
pero sigue numpy.ndarray
errores correctamente con las clases definidas en python. ¿Por qué la implementación de uno de los tipos integrados rompería la funcionalidad de las clases abstractas?
Sorprendentemente, la prueba que evita la object.__new__
instancias de clases abstractas ocurre en el object.__new__
, en lugar de cualquier cosa definida por el propio módulo abc
:
static PyObject * object_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { ... if (type->tp_flags & Py_TPFLAGS_IS_ABSTRACT) { ... PyErr_Format(PyExc_TypeError, "Can't instantiate abstract class %s " "with abstract methods %U", type->tp_name, joined);
(¿Casi?) Todos los tipos incorporados que no son object
suministran un __new__
diferente que anula el object.__new__
y no llama al object.__new__
. Cuando se hereda de forma múltiple de un tipo incorporado que no es un object
, se hereda su método __new__
, omitiendo la verificación del método abstracto.
No veo nada acerca de __new__
o la herencia múltiple de los tipos incorporados en la documentación de abc
. La documentación podría utilizar la mejora aquí.
Parece un poco extraño que usaran una metaclase para la implementación de ABC, haciendo que sea un desastre usar otras metaclases con clases abstractas, y luego poner el control crucial en el código del lenguaje central que no tiene nada que ver con abc
y se ejecuta para ambos Clases abstractas y no abstractas.
Hay un informe para este problema en el rastreador de problemas que ha estado languideciendo desde 2009.