Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple Inheritance in Python C API

How do I create a type using the Python C API that inherits from multiple other types?

The Python documentation includes an example of a type that inherits from one other type, but there is no example or mention of multiple inheritance I could find.

like image 386
tbodt Avatar asked Oct 19 '25 15:10

tbodt


2 Answers

The C API does not support multiple inheritance. You'd have to call PyType_Type yourself, simulating a standard Python class statement. This is documented under the C API section on specifying a base type for an extension type:

PyTypeObject* PyTypeObject.tp_base

An optional pointer to a base type from which type properties are inherited. At this level, only single inheritance is supported; multiple inheritance require dynamically creating a type object by calling the metatype.

This field is not inherited by subtypes (obviously), but it defaults to &PyBaseObject_Type (which to Python programmers is known as the type object).

like image 159
user2357112 supports Monica Avatar answered Oct 21 '25 05:10

user2357112 supports Monica


Let's say you have a module named test, with the following classes:

class A:
    a = 1

class B:
    b = 2

And the idea is to create a new class C which inherits from A and B:

import test

class C(test.A, test.B):
    pass

The specification on PyTypeObject.tp_base says that it does not support multiple bases classes and that you have to create the class "by calling the metatype". In this case, the metatype is type, so the class can be created this way:

from test import A, B

C = type("C", (A, B), {})

Translating that to C is straightforward, although a bit verbose:

// from test import A, B
PyObject* test_module = PyImport_ImportModuleNoBlock("test");
if (test_module == NULL) return NULL;
PyObject* ClassA = PyObject_GetAttrString(test_module, "A");
if (ClassA == NULL) return NULL;
PyObject* ClassB = PyObject_GetAttrString(test_module, "B");
if (ClassB == NULL) return NULL;

// name, bases, classdict = "C", (A, B), {}
PyObject *name = PyUnicode_FromString("C");
if (name == NULL) return NULL;
PyObject *bases = PyTuple_Pack(2, ClassA, ClassB);
if (bases == NULL) return NULL;
PyObject *classdict = PyDict_New();
if (dict == NULL) return NULL;

// C = type(name, bases, classdict)
PyObject *ClassC = PyObject_CallObject(
    (PyObject*)&PyType_Type, PyTuple_Pack(3, name, bases, classdict));
if (ClassC == NULL) return NULL;
if (PyModule_AddObject(m, "C", ClassC)) return NULL;
like image 26
zvone Avatar answered Oct 21 '25 05:10

zvone