Calling Parent Class _Init_ with Multiple Inheritance, What's the Right Way

Calling parent class __init__ with multiple inheritance, what's the right way?

Both ways work fine. The approach using super() leads to greater flexibility for subclasses.

In the direct call approach, C.__init__ can call both A.__init__ and B.__init__.

When using super(), the classes need to be designed for cooperative multiple inheritance where C calls super, which invokes A's code which will also call super which invokes B's code. See http://rhettinger.wordpress.com/2011/05/26/super-considered-super for more detail on what can be done with super.

[Response question as later edited]

So it seems that unless I know/control the init's of the classes I
inherit from (A and B) I cannot make a safe choice for the class I'm
writing (C).

The referenced article shows how to handle this situation by adding a wrapper class around A and B. There is a worked-out example in the section titled "How to Incorporate a Non-cooperative Class".

One might wish that multiple inheritance were easier, letting you effortlessly compose Car and Airplane classes to get a FlyingCar, but the reality is that separately designed components often need adapters or wrappers before fitting together as seamlessly as we would like :-)

One other thought: if you're unhappy with composing functionality using multiple inheritance, you can use composition for complete control over which methods get called on which occasions.

Python - Calling __init__ for multiple parent classes

This depends entirely on whether Y and Z were designed to work cooperatively with multiple inheritance. If they are, then you should be able to just use super:

class X(Y, Z):
def __init__(self):
super().__init__()

When I say that Y and Z need to be designed to work cooperatively with multiple inheritance, I mean that they must also call super in their __init__ methods:

class Y:
def __init__(self):
print('Y.__init__')
super().__init__()

class Z:
def __init__(self):
print('Z.__init__')
super().__init__()

They should also have some strategy for handling different arguments since (particularly the constructor) of a class frequently differ in which keyword arguments that they accept. At this point, it's worth reading Super considered super! and Super considered harmful to understand some common idioms and pitfalls regarding super.

If you don't know whether Y and Z were designed for cooperative multiple inheritance, then the safest recourse is to assume that they weren't.


If the classes aren't designed to work with cooperative multiple inheritance, then you need to start stepping lightly (you're in dangerous waters). You can call each __init__ individually:

class X(Y, Z):
def __init__(self):
Y.__init__(self)
Z.__init__(self)

But really it's probably better to not use multiple inheritance and instead choose a different paradigm (e.g. composition).

How does Python's super() work with multiple inheritance?

This is detailed with a reasonable amount of detail by Guido himself in his blog post Method Resolution Order (including two earlier attempts).

In your example, Third() will call First.__init__. Python looks for each attribute in the class's parents as they are listed left to right. In this case, we are looking for __init__. So, if you define

class Third(First, Second):
...

Python will start by looking at First, and, if First doesn't have the attribute, then it will look at Second.

This situation becomes more complex when inheritance starts crossing paths (for example if First inherited from Second). Read the link above for more details, but, in a nutshell, Python will try to maintain the order in which each class appears on the inheritance list, starting with the child class itself.

So, for instance, if you had:

class First(object):
def __init__(self):
print "first"

class Second(First):
def __init__(self):
print "second"

class Third(First):
def __init__(self):
print "third"

class Fourth(Second, Third):
def __init__(self):
super(Fourth, self).__init__()
print "that's it"

the MRO would be [Fourth, Second, Third, First].

By the way: if Python cannot find a coherent method resolution order, it'll raise an exception, instead of falling back to behavior which might surprise the user.

Example of an ambiguous MRO:

class First(object):
def __init__(self):
print "first"

class Second(First):
def __init__(self):
print "second"

class Third(First, Second):
def __init__(self):
print "third"

Should Third's MRO be [First, Second] or [Second, First]? There's no obvious expectation, and Python will raise an error:

TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution order (MRO) for bases Second, First

Why do the examples above lack super() calls? The point of the examples is to show how the MRO is constructed. They are not intended to print "first\nsecond\third" or whatever. You can – and should, of course, play around with the example, add super() calls, see what happens, and gain a deeper understanding of Python's inheritance model. But my goal here is to keep it simple and show how the MRO is built. And it is built as I explained:

>>> Fourth.__mro__
(<class '__main__.Fourth'>,
<class '__main__.Second'>, <class '__main__.Third'>,
<class '__main__.First'>,
<type 'object'>)

Should __init__() call the parent class's __init__()?

In Python, calling the super-class' __init__ is optional. If you call it, it is then also optional whether to use the super identifier, or whether to explicitly name the super class:

object.__init__(self)

In case of object, calling the super method is not strictly necessary, since the super method is empty. Same for __del__.

On the other hand, for __new__, you should indeed call the super method, and use its return as the newly-created object - unless you explicitly want to return something different.

Understanding Python super() with __init__() methods

super() lets you avoid referring to the base class explicitly, which can be nice. But the main advantage comes with multiple inheritance, where all sorts of fun stuff can happen. See the standard docs on super if you haven't already.

Note that the syntax changed in Python 3.0: you can just say super().__init__() instead of super(ChildB, self).__init__() which IMO is quite a bit nicer. The standard docs also refer to a guide to using super() which is quite explanatory.

How does multiple inheritance work with the super() and different __init__() arguments?

For question 2, you need to call super in each class:

class First(object):
def __init__(self):
super(First, self).__init__()
print "first"

class Second(object):
def __init__(self):
super(Second, self).__init__()
print "second"

class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print "that's it"

For question 3, that can't be done, your method needs to have the same signature. But you could just ignore some parameters in the parent clases or use keywords arguments.

When inheriting directly from `object`, should I call super().__init__()?

Should classes that inherit directly from object call super().__init__()?

You seem to search a simple "yes or no" answer for this question, but unfortunately the answer is "it depends". Furthermore, when deciding if you should call super().__init__(), it is somewhat irrelevant whether or not a class inherits directly from object. What is invariant is that, if object.__init__ is called, it should be called without arguments - since object.__init__ does not accept arguments.

Practically, in cooperative inheritance situations, this means you must ensure that all arguments are consumed before object.__init__ gets invoked. It does not mean you should try to avoid object.__init__ being invoked. Here is an example of consuming args before invoking super, the response and request context has been popped out of the mutable mapping kwargs.

I mentioned earlier that whether or not a class inherits directly from object is a red herring1. But I didn't mention yet what should motivate this design decision: You should call super init [read: super anymethod] if you want the MRO to continue to be searched for other initializers [read: other anymethods]. You should not invoke super if you want to indicate the MRO search should be stopped here.

Why does object.__init__ exist at all, if it doesn't do anything? Because it does do something: ensures it was called without arguments. The presence of arguments likely indicates a bug2. object also serves the purpose of stopping the chain of super calls - somebody has to not call super, otherwise we recurse infinitely. You can stop it explicitly yourself, earlier, by not invoking super. If you don't, object will serve as the final link and stop the chain for you.

Class MRO is determined at compile time, which is generally when a class is defined / when the module is imported. However, note that the use of super involves many chances for runtime branching. You have to consider:

  • Which arguments a method of the super is called with (i.e. which arguments you want to forward along the MRO)
  • Whether or not super is called at all (sometimes you want to intentionally break the chain)
  • Which arguments, if any, the super itself is created with (there is an advanced use case described below)
  • Whether to call a proxied method before or after the code in your own method (put super first if need to access some state the proxied methods set up, put super last if you're setting up some state that the proxied methods rely on being there already - in some cases you even want to put super in the middle somewhere!)
  • The question asks mostly about __init__, but don't forget that super can be used with any other method, too

In rare circumstances, you might conditionally invoke a super call. You might check whether your super() instance has this or that attribute, and base some logic around the result. Or, you might invoke super(OtherClass, self) to explicitly "step over" a link and manually traverse the MRO for this section. Yes, if the default behaviour is not what you wanted, you can hijack the MRO! What all these diabolical ideas have in common is an understanding of the C3 linearization algorithm, how Python makes an MRO, and how super itself uses the MRO. Python's implementation was more or less lifted from another programming language, where super was named next-method. Honestly super is a super-bad name in Python because it causes a common misconception amongst beginners that you're always invoking "up" to one of the parent classes, I wish they had chosen a better name.

When defining an inheritance hierarchy, the interpreter can not know whether you wanted to reuse some other classes existing functionality or to replace it with an alternate implementation, or something else. Either decision could be a valid and practical design. If there was a hard and fast rule about when and how super should be invoked, it would not be left to the programmer to choose - the language would take the decision out of your hands and just do the right thing automatically. I hope that sufficiently explains that invoking super in __init__ is not a simple yes/no question.

If yes, how would you correctly initialize SuperFoo?

(Source for Foo, SuperFoo etc in this revision of the question)

For the purposes of answering this part, I will assume the __init__ methods shown in the MCVE actually need to do some initialization (perhaps you could add placeholder comments in the question's MCVE code to that effect). Don't define an __init__ at all if the only you do is call super with same arguments, there's no point. Don't define an __init__ that's just pass, unless you intentionally mean to halt the MRO traversal there (in which case a comment is certainly warranted!).

Firstly, before we discuss the SuperFoo, let me say that NoSuperFoo looks like an incomplete or bad design. How do you pass the foo argument to Foo's init? The foo init value of 3 was hardcoded. It might be OK to hardcode (or otherwise automatically determine) foo's init value, but then you should probably be doing composition not inheritance.

As for SuperFoo, it inherits SuperCls and Foo. SuperCls looks intended for inheritance, Foo does not. That means you may have some work to do, as pointed out in super harmful. One way forward, as discussed in Raymond's blog, is writing adapters.

class FooAdapter:
def __init__(self, **kwargs):
foo_arg = kwargs.pop('foo')
# can also use kwargs['foo'] if you want to leave the responsibility to remove 'foo' to someone else
# can also use kwargs.pop('foo', 'foo-default') if you want to make this an optional argument
# can also use kwargs.get('foo', 'foo-default') if you want both of the above
self._the_foo_instance = Foo(foo_arg)
super().__init__(**kwargs)

# add any methods, wrappers, or attribute access you need

@property
def foo():
# or however you choose to expose Foo functionality via the adapter
return self._the_foo_instance.foo

Note that FooAdapter has a Foo, not FooAdapter is a Foo. This is not the only possible design choice. However, if you are inheriting like class FooParent(Foo), then you're implying a FooParent is a Foo, and can be used in any place where a Foo would otherwise be - it's often easier to avoid violations of LSP by using composition. SuperCls should also cooperate by allowing **kwargs:

class SuperCls:
def __init__(self, **kwargs):
# some other init code here
super().__init__(**kwargs)

Maybe SuperCls is also out of your control and you have to adapt it too, so be it. The point is, this is a way to re-use code, by adjusting the interfaces so that the signatures are matching. Assuming everyone is cooperating well and consuming what they need, eventually super().__init__(**kwargs) will proxy to object.__init__(**{}).

Since 99% of classes I've seen don't use **kwargs in their constructor, does that mean 99% of python classes are implemented incorrectly?

No, because YAGNI. Do 99% of classes need to immediately support 100% general dependency-injection with all the bells and whistles, before they are useful? Are they broken if they don't? As an example, consider the OrderedCounter recipe given in the collections docs. Counter.__init__ accepts *args and **kwargs, but doesn't proxy them in the super init call. If you wanted to use one of those arguments, well tough luck, you've got to override __init__ and intercept them. OrderedDict isn't defined cooperatively at all, really, some parent calls are hardcoded to dict - and the __init__ of anything next in line isn't invoked, so any MRO traversal would be stopped in its tracks there. If you accidentally defined it as OrderedCounter(OrderedDict, Counter) instead of OrderedCounter(Counter, OrderedDict) the metaclass bases would still be able to create a consistent MRO, but the class just wouldn't work at all as an ordered counter.

In spite of all these shortcomings, the OrderedCounter recipe works as advertised, because the MRO is traversed as designed for the intended use-case. So, you don't even need to do cooperative inheritance 100% correctly in order to implement a dependency-injection. The moral of the story is that perfection is the enemy of progress (or, practicality beats purity). If you want to cram MyWhateverClass into any crazy inheritance tree you can dream up, go ahead, but it is up to you to write the necessary scaffolding to allow that. As usual, Python will not prevent you to implement it in whatever hacky way is good enough to work.

1You're always inheriting from object, whether you wrote it in the class declaration or not. Many open source codebases will inherit from object explicitly anyway in order to be cross-compatible with 2.7 runtimes.

2This point is explained in greater detail, along with the subtle relationship between __new__ and __init__, in CPython sources here.

Inheritance and Overriding __init__ in python

The book is a bit dated with respect to subclass-superclass calling. It's also a little dated with respect to subclassing built-in classes.

It looks like this nowadays:

class FileInfo(dict):
"""store file metadata"""
def __init__(self, filename=None):
super(FileInfo, self).__init__()
self["name"] = filename

Note the following:

  1. We can directly subclass built-in classes, like dict, list, tuple, etc.

  2. The super function handles tracking down this class's superclasses and calling functions in them appropriately.



Related Topics



Leave a reply



Submit