I have a Python class AbstractFoo
which uses @abc.abstractmethod
to define 2 abstract methods.
For more performance the bulk of the work is implemented in C as a Python extension class CFoo
, so what I wanted to do is to inherit that extension class from AbstractFoo
.
However I haven't found a way to do this and went a different route: Implement it as a "normal" class in C and have a class ConcreteFoo(AbstractFoo, CFoo)
which inherits from the abstract Python and the extension class.
However now the "protection" from the ABC didn't kick in: No error was thrown even when the methods were missing. I found a solution in https://stackoverflow.com/a/20440259/1930508 and added a new new
method instead of PyType_GenericNew
(ignore the ref counts of the empty_* parts for now):
static PyObject* CFoo_new(PyTypeObject* type, PyObject*, PyObject*)
{
auto* empty_tuple(PyTuple_New(0));
if (!empty_tuple)
return nullptr;
auto* empty_dict(PyDict_New());
if (!empty_dict)
return nullptr;
return PyBaseObject_Type.tp_new(type, empty_tuple.get(), empty_dict.get());
}
But now the ABC check always triggers: TypeError: Can't instantiate abstract class ConcreteFoo with abstract methods ...
But checking dir(ConcreteFoo)
shows the methods it complains about.
Is there a way to have an ABC-checked subclass of my AbstractFoo
where the methods are implemented in C?
Edit: Some more code:
class AbstractFoo(abc.ABC):
@abc.abstractmethod
def register(self):
pass
# ...
#...
class ConcreteFoo(AbstractFoo, CFoo):
def __init__(self, arg):
AbstractFoo.__init__(self)
CFoo.__init__(self, arg)
You have to make CFoo
the first base class. This has little to do with the C-API and applies to a pure Python version too: if you define
class CFoo:
def register(self):
return "CFoo.register"
then
class ConcreteFoo(AbstractFoo, CFoo):
fails, but
class ConcreteFoo(CFoo, AbstractFoo):
works.
This makes sense in terms of the __mro__
and you can test this by making register
not be an abstract method. You'll find that the method of the first base class in used in preference. When the first base class is AbstractFoo
this is an abstract method that's being found first, so it fails.