I originally asked this question on the Python capi-sig list: How to pass arguments to tp_new and tp_init from subtypes?
I'm reading the Python PEP-253 on subtyping and there are plenty of good recommendations on how to structure the types, call tp_new
and tp_init
slots, etc.
But, it lacks an important note on passing arguments from sub to super type. It seems the PEP-253 is unfinished as per the note:
(XXX There should be a paragraph or two about argument passing here.)
So, I'm trying to extrapolate some strategies well known from the Python classes subtyping, especially techniques that each level strips-off arguments, etc.
I'm looking for techniques to achieve similar effect to this, but using plain Python C API (3.x):
class Shape:
def __init__(self, shapename, **kwds):
self.shapename = shapename
super().__init__(**kwds)
class ColoredShape(Shape):
def __init__(self, color, **kwds):
self.color = color
super().__init__(**kwds)
What would be the equivalent in Python C API?
How to deal with similar situation but with arguments specific to derived class expected in different order?
It is arguments given at the end of the args tuple (or kwds
dict, I assume principle would be same).
Here is some (pseudo-)code that illustrates the situation:
class Base:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
class Derived(Base):
def __init__(self, x, y, a):
self.a = a
super().__init__(x, y, None):
Note, if the a
was expected first:
Derived.__init__(self, a, x, y)
it would be similar situation to the Shape
and ColoredShape
above.
It would also be easier to deal with, I assume.
Could anyone help to figure out the missing XXX comment mentioned above and correct technique for passing arguments from subtype up to super type(s) on construction?
UPDATE 2012-07-17:
Inspired by ecatmur's answer below I looked through Python 3 sources and I found defdict_init
constructor of collections.defaultdict type object interesting. The type is derived from PyDictObject
and its constructor takes additional argument of default_factory
. The constructor signature in Python class is this:
class collections.defaultdict([default_factory[, ...]])
Now, here is how the default_factory
is stripped from original args
tuple, so the rest of arguments is forwarded to the tp_init
of base type, it is PyDictObject
:
int result;
PyObject *newargs;
Py_ssize_t n = PyTuple_GET_SIZE(args);
...
newargs = PySequence_GetSlice(args, 1, n);
...
result = PyDict_Type.tp_init(self, newargs, kwds);
Note, this snipped presence only the relevant part of the defdict_init
function.