Chain-calling parent initialisers in python [duplicate] - python

This question already has answers here:
How to invoke the super constructor in Python?
(7 answers)
Closed 6 years ago.
Consider this - a base class A, class B inheriting from A, class C inheriting from B. What is a generic way to call a parent class initialiser in an initialiser? If this still sounds too vague, here's some code.
class A(object):
def __init__(self):
print "Initialiser A was called"
class B(A):
def __init__(self):
super(B,self).__init__()
print "Initialiser B was called"
class C(B):
def __init__(self):
super(C,self).__init__()
print "Initialiser C was called"
c = C()
This is how I do it now. But it still seems a bit too non-generic - you still must pass a correct type by hand.
Now, I've tried using self.__class__ as a first argument to super(), but, obviously it doesn't work - if you put it in the initialiser for C - fair enough, B's initialiser gets called. If you do the same in B, "self" still points to an instance of C so you end up calling B's initialiser again (this ends in an infinite recursion).
There is no need to think about diamond inheritance for now, I am just interested in solving this specific problem.

Python 3 includes an improved super() which allows use like this:
super().__init__(args)

The way you are doing it is indeed the recommended one (for Python 2.x).
The issue of whether the class is passed explicitly to super is a matter of style rather than functionality. Passing the class to super fits in with Python's philosophy of "explicit is better than implicit".

You can simply write :
class A(object):
def __init__(self):
print "Initialiser A was called"
class B(A):
def __init__(self):
A.__init__(self)
# A.__init__(self,<parameters>) if you want to call with parameters
print "Initialiser B was called"
class C(B):
def __init__(self):
# A.__init__(self) # if you want to call most super class...
B.__init__(self)
print "Initialiser C was called"

Related

How to target parent constructors separately with different sets of parameters from a child class constructor in Python using super() [duplicate]

Say I have a multiple inheritance scenario:
class A(object):
# code for A here
class B(object):
# code for B here
class C(A, B):
def __init__(self):
# What's the right code to write here to ensure
# A.__init__ and B.__init__ get called?
There's two typical approaches to writing C's __init__:
(old-style) ParentClass.__init__(self)
(newer-style) super(DerivedClass, self).__init__()
However, in either case, if the parent classes (A and B) don't follow the same convention, then the code will not work correctly (some may be missed, or get called multiple times).
So what's the correct way again? It's easy to say "just be consistent, follow one or the other", but if A or B are from a 3rd party library, what then? Is there an approach that can ensure that all parent class constructors get called (and in the correct order, and only once)?
Edit: to see what I mean, if I do:
class A(object):
def __init__(self):
print("Entering A")
super(A, self).__init__()
print("Leaving A")
class B(object):
def __init__(self):
print("Entering B")
super(B, self).__init__()
print("Leaving B")
class C(A, B):
def __init__(self):
print("Entering C")
A.__init__(self)
B.__init__(self)
print("Leaving C")
Then I get:
Entering C
Entering A
Entering B
Leaving B
Leaving A
Entering B
Leaving B
Leaving C
Note that B's init gets called twice. If I do:
class A(object):
def __init__(self):
print("Entering A")
print("Leaving A")
class B(object):
def __init__(self):
print("Entering B")
super(B, self).__init__()
print("Leaving B")
class C(A, B):
def __init__(self):
print("Entering C")
super(C, self).__init__()
print("Leaving C")
Then I get:
Entering C
Entering A
Leaving A
Leaving C
Note that B's init never gets called. 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 answer to your question depends on one very important aspect: Are your base classes designed for multiple inheritance?
There are 3 different scenarios:
The base classes are unrelated, standalone classes.
If your base classes are separate entities that are capable of functioning independently and they don't know each other, they're not designed for multiple inheritance. Example:
class Foo:
def __init__(self):
self.foo = 'foo'
class Bar:
def __init__(self, bar):
self.bar = bar
Important: Notice that neither Foo nor Bar calls super().__init__()! This is why your code didn't work correctly. Because of the way diamond inheritance works in python, classes whose base class is object should not call super().__init__(). As you've noticed, doing so would break multiple inheritance because you end up calling another class's __init__ rather than object.__init__(). (Disclaimer: Avoiding super().__init__() in object-subclasses is my personal recommendation and by no means an agreed-upon consensus in the python community. Some people prefer to use super in every class, arguing that you can always write an adapter if the class doesn't behave as you expect.)
This also means that you should never write a class that inherits from object and doesn't have an __init__ method. Not defining a __init__ method at all has the same effect as calling super().__init__(). If your class inherits directly from object, make sure to add an empty constructor like so:
class Base(object):
def __init__(self):
pass
Anyway, in this situation, you will have to call each parent constructor manually. There are two ways to do this:
Without super
class FooBar(Foo, Bar):
def __init__(self, bar='bar'):
Foo.__init__(self) # explicit calls without super
Bar.__init__(self, bar)
With super
class FooBar(Foo, Bar):
def __init__(self, bar='bar'):
super().__init__() # this calls all constructors up to Foo
super(Foo, self).__init__(bar) # this calls all constructors after Foo up
# to Bar
Each of these two methods has its own advantages and disadvantages. If you use super, your class will support dependency injection. On the other hand, it's easier to make mistakes. For example if you change the order of Foo and Bar (like class FooBar(Bar, Foo)), you'd have to update the super calls to match. Without super you don't have to worry about this, and the code is much more readable.
One of the classes is a mixin.
A mixin is a class that's designed to be used with multiple inheritance. This means we don't have to call both parent constructors manually, because the mixin will automatically call the 2nd constructor for us. Since we only have to call a single constructor this time, we can do so with super to avoid having to hard-code the parent class's name.
Example:
class FooMixin:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # forwards all unused arguments
self.foo = 'foo'
class Bar:
def __init__(self, bar):
self.bar = bar
class FooBar(FooMixin, Bar):
def __init__(self, bar='bar'):
super().__init__(bar) # a single call is enough to invoke
# all parent constructors
# NOTE: `FooMixin.__init__(self, bar)` would also work, but isn't
# recommended because we don't want to hard-code the parent class.
The important details here are:
The mixin calls super().__init__() and passes through any arguments it receives.
The subclass inherits from the mixin first: class FooBar(FooMixin, Bar). If the order of the base classes is wrong, the mixin's constructor will never be called.
All base classes are designed for cooperative inheritance.
Classes designed for cooperative inheritance are a lot like mixins: They pass through all unused arguments to the next class. Like before, we just have to call super().__init__() and all parent constructors will be chain-called.
Example:
class CoopFoo:
def __init__(self, **kwargs):
super().__init__(**kwargs) # forwards all unused arguments
self.foo = 'foo'
class CoopBar:
def __init__(self, bar, **kwargs):
super().__init__(**kwargs) # forwards all unused arguments
self.bar = bar
class CoopFooBar(CoopFoo, CoopBar):
def __init__(self, bar='bar'):
super().__init__(bar=bar) # pass all arguments on as keyword
# arguments to avoid problems with
# positional arguments and the order
# of the parent classes
In this case, the order of the parent classes doesn't matter. We might as well inherit from CoopBar first, and the code would still work the same. But that's only true because all arguments are passed as keyword arguments. Using positional arguments would make it easy to get the order of the arguments wrong, so it's customary for cooperative classes to accept only keyword arguments.
This is also an exception to the rule I mentioned earlier: Both CoopFoo and CoopBar inherit from object, but they still call super().__init__(). If they didn't, there would be no cooperative inheritance.
Bottom line: The correct implementation depends on the classes you're inheriting from.
The constructor is part of a class's public interface. If the class is designed as a mixin or for cooperative inheritance, that must be documented. If the docs don't mention anything of the sort, it's safe to assume that the class isn't designed for cooperative multiple inheritance.
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.
Either approach ("new style" or "old style") will work if you have control over the source code for A and B. Otherwise, use of an adapter class might be necessary.
Source code accessible: Correct use of "new style"
class A(object):
def __init__(self):
print("-> A")
super(A, self).__init__()
print("<- A")
class B(object):
def __init__(self):
print("-> B")
super(B, self).__init__()
print("<- B")
class C(A, B):
def __init__(self):
print("-> C")
# Use super here, instead of explicit calls to __init__
super(C, self).__init__()
print("<- C")
>>> C()
-> C
-> A
-> B
<- B
<- A
<- C
Here, method resolution order (MRO) dictates the following:
C(A, B) dictates A first, then B. MRO is C -> A -> B -> object.
super(A, self).__init__() continues along the MRO chain initiated in C.__init__ to B.__init__.
super(B, self).__init__() continues along the MRO chain initiated in C.__init__ to object.__init__.
You could say that this case is designed for multiple inheritance.
Source code accessible: Correct use of "old style"
class A(object):
def __init__(self):
print("-> A")
print("<- A")
class B(object):
def __init__(self):
print("-> B")
# Don't use super here.
print("<- B")
class C(A, B):
def __init__(self):
print("-> C")
A.__init__(self)
B.__init__(self)
print("<- C")
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C
Here, MRO does not matter, since A.__init__ and B.__init__ are called explicitly. class C(B, A): would work just as well.
Although this case is not "designed" for multiple inheritance in the new style as the previous one was, multiple inheritance is still possible.
Now, what if A and B are from a third party library - i.e., you have no control over the source code for A and B? The short answer: You must design an adapter class that implements the necessary super calls, then use an empty class to define the MRO (see Raymond Hettinger's article on super - especially the section, "How to Incorporate a Non-cooperative Class").
Third-party parents: A does not implement super; B does
class A(object):
def __init__(self):
print("-> A")
print("<- A")
class B(object):
def __init__(self):
print("-> B")
super(B, self).__init__()
print("<- B")
class Adapter(object):
def __init__(self):
print("-> C")
A.__init__(self)
super(Adapter, self).__init__()
print("<- C")
class C(Adapter, B):
pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C
Class Adapter implements super so that C can define the MRO, which comes into play when super(Adapter, self).__init__() is executed.
And what if it's the other way around?
Third-party parents: A implements super; B does not
class A(object):
def __init__(self):
print("-> A")
super(A, self).__init__()
print("<- A")
class B(object):
def __init__(self):
print("-> B")
print("<- B")
class Adapter(object):
def __init__(self):
print("-> C")
super(Adapter, self).__init__()
B.__init__(self)
print("<- C")
class C(Adapter, A):
pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C
Same pattern here, except the order of execution is switched in Adapter.__init__; super call first, then explicit call. Notice that each case with third-party parents requires a unique adapter class.
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).
Although you can handle the cases where you don't control the source code of A and B by using an adapter class, it is true that you must know how the init's of the parent classes implement super (if at all) in order to do so.
As Raymond said in his answer, a direct call to A.__init__ and B.__init__ works fine, and your code would be readable.
However, it does not use the inheritance link between C and those classes. Exploiting that link gives you more consistancy and make eventual refactorings easier and less error-prone. An example of how to do that:
class C(A, B):
def __init__(self):
print("entering c")
for base_class in C.__bases__: # (A, B)
base_class.__init__(self)
print("leaving c")
This article helps to explain cooperative multiple inheritance:
The wonders of cooperative inheritance, or using super in Python 3
It mentions the useful method mro() that shows you the method resolution order. In your second example, where you call super in A, the super call continues on in MRO. The next class in the order is B, this is why B's init is called the first time.
Here's a more technical article from the official Python site:
The Python 2.3 Method Resolution Order
If you are multiply sub-classing classes from third party libraries, then no, there is no blind approach to calling the base class __init__ methods (or any other methods) that actually works regardless of how the base classes are programmed.
super makes it possible to write classes designed to cooperatively implement methods as part of complex multiple inheritance trees which need not be known to the class author. But there's no way to use it to correctly inherit from arbitrary classes that may or may not use super.
Essentially, whether a class is designed to be sub-classed using super or with direct calls to the base class is a property which is part of the class' "public interface", and it should be documented as such. If you're using third-party libraries in the way that the library author expected and the library has reasonable documentation, it would normally tell you what you are required to do to subclass particular things. If not, then you'll have to look at the source code for the classes you're sub-classing and see what their base-class-invocation convention is. If you're combining multiple classes from one or more third-party libraries in a way that the library authors didn't expect, then it may not be possible to consistently invoke super-class methods at all; if class A is part of a hierarchy using super and class B is part of a hierarchy that doesn't use super, then neither option is guaranteed to work. You'll have to figure out a strategy that happens to work for each particular case.
I added a small utility library, supers, which makes this kind of scenario simpler to handle. It works as follows:
class A(object):
def __init__(self):
print("Entering A")
print("Leaving A")
class B(object):
def __init__(self):
print("Entering B")
super(B, self).__init__()
print("Leaving B")
class C(A, B):
def __init__(self):
print("Entering C")
supers(self).__init__()
print("Leaving C")
Output when creating C:
Entering C
Entering A
Leaving A
Entering B
Leaving B
Leaving C
Here is how I have implemented the multiple inheritance in Python 3 using super()
class A:
def __init__(self, a, b, **kwargs):
print("Class A initiallised")
self.a = a
self.b = b
super().__init__(**kwargs)
print("Class A initiallisation done")
def __str__(self):
return f"{self.a} and {self.b}"
def display_a(self):
return f"{self.a} and {self.b}"
class C:
def __init__(self, c, d, **kwargs):
print("Class C initiallised")
self.c = c
self.d = d
super().__init__(**kwargs)
print("class c initiallisation done")
def __str__(self):
return f"{self.c} and {self.d}"
def display_c(self):
return f"{self.c} and {self.d}"
class D(A,C):
def __init__(self, e, **kwargs):
print("Class D initiallised")
super().__init__(**kwargs)
self.e = e
print("Class D initiallisation done")
def __str__(self):
return f"{self.e} is e,{self.b} is b,{self.a} is a,{self.d} is d,{self.c} is c"
if __name__ == "__main__":
d = D(a=12, b=13, c=14, d=15, e=16)
print(d)
d.display_c()
d.display_a()
Here is how I have implemented the super method in Python inheritance and achieved the required solution:
class A:
def __init__(self):
print("from A")
class B:
def __init__(self):
print("from B")
class C(A, B):
def __init__(self):
A.__init__(self)
B.__init__(self)
print("from C")
c = C()
Firstly, suppose you got the MRO chain
From the lowest level subclass init method on, any class which using super() method would jump into corresponding chain position, as any class which not using super() method would jump out corresponding chain position.
It follows the MRO rule and A init is called.

Multiple inheritance in python using function super() [duplicate]

Say I have a multiple inheritance scenario:
class A(object):
# code for A here
class B(object):
# code for B here
class C(A, B):
def __init__(self):
# What's the right code to write here to ensure
# A.__init__ and B.__init__ get called?
There's two typical approaches to writing C's __init__:
(old-style) ParentClass.__init__(self)
(newer-style) super(DerivedClass, self).__init__()
However, in either case, if the parent classes (A and B) don't follow the same convention, then the code will not work correctly (some may be missed, or get called multiple times).
So what's the correct way again? It's easy to say "just be consistent, follow one or the other", but if A or B are from a 3rd party library, what then? Is there an approach that can ensure that all parent class constructors get called (and in the correct order, and only once)?
Edit: to see what I mean, if I do:
class A(object):
def __init__(self):
print("Entering A")
super(A, self).__init__()
print("Leaving A")
class B(object):
def __init__(self):
print("Entering B")
super(B, self).__init__()
print("Leaving B")
class C(A, B):
def __init__(self):
print("Entering C")
A.__init__(self)
B.__init__(self)
print("Leaving C")
Then I get:
Entering C
Entering A
Entering B
Leaving B
Leaving A
Entering B
Leaving B
Leaving C
Note that B's init gets called twice. If I do:
class A(object):
def __init__(self):
print("Entering A")
print("Leaving A")
class B(object):
def __init__(self):
print("Entering B")
super(B, self).__init__()
print("Leaving B")
class C(A, B):
def __init__(self):
print("Entering C")
super(C, self).__init__()
print("Leaving C")
Then I get:
Entering C
Entering A
Leaving A
Leaving C
Note that B's init never gets called. 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 answer to your question depends on one very important aspect: Are your base classes designed for multiple inheritance?
There are 3 different scenarios:
The base classes are unrelated, standalone classes.
If your base classes are separate entities that are capable of functioning independently and they don't know each other, they're not designed for multiple inheritance. Example:
class Foo:
def __init__(self):
self.foo = 'foo'
class Bar:
def __init__(self, bar):
self.bar = bar
Important: Notice that neither Foo nor Bar calls super().__init__()! This is why your code didn't work correctly. Because of the way diamond inheritance works in python, classes whose base class is object should not call super().__init__(). As you've noticed, doing so would break multiple inheritance because you end up calling another class's __init__ rather than object.__init__(). (Disclaimer: Avoiding super().__init__() in object-subclasses is my personal recommendation and by no means an agreed-upon consensus in the python community. Some people prefer to use super in every class, arguing that you can always write an adapter if the class doesn't behave as you expect.)
This also means that you should never write a class that inherits from object and doesn't have an __init__ method. Not defining a __init__ method at all has the same effect as calling super().__init__(). If your class inherits directly from object, make sure to add an empty constructor like so:
class Base(object):
def __init__(self):
pass
Anyway, in this situation, you will have to call each parent constructor manually. There are two ways to do this:
Without super
class FooBar(Foo, Bar):
def __init__(self, bar='bar'):
Foo.__init__(self) # explicit calls without super
Bar.__init__(self, bar)
With super
class FooBar(Foo, Bar):
def __init__(self, bar='bar'):
super().__init__() # this calls all constructors up to Foo
super(Foo, self).__init__(bar) # this calls all constructors after Foo up
# to Bar
Each of these two methods has its own advantages and disadvantages. If you use super, your class will support dependency injection. On the other hand, it's easier to make mistakes. For example if you change the order of Foo and Bar (like class FooBar(Bar, Foo)), you'd have to update the super calls to match. Without super you don't have to worry about this, and the code is much more readable.
One of the classes is a mixin.
A mixin is a class that's designed to be used with multiple inheritance. This means we don't have to call both parent constructors manually, because the mixin will automatically call the 2nd constructor for us. Since we only have to call a single constructor this time, we can do so with super to avoid having to hard-code the parent class's name.
Example:
class FooMixin:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # forwards all unused arguments
self.foo = 'foo'
class Bar:
def __init__(self, bar):
self.bar = bar
class FooBar(FooMixin, Bar):
def __init__(self, bar='bar'):
super().__init__(bar) # a single call is enough to invoke
# all parent constructors
# NOTE: `FooMixin.__init__(self, bar)` would also work, but isn't
# recommended because we don't want to hard-code the parent class.
The important details here are:
The mixin calls super().__init__() and passes through any arguments it receives.
The subclass inherits from the mixin first: class FooBar(FooMixin, Bar). If the order of the base classes is wrong, the mixin's constructor will never be called.
All base classes are designed for cooperative inheritance.
Classes designed for cooperative inheritance are a lot like mixins: They pass through all unused arguments to the next class. Like before, we just have to call super().__init__() and all parent constructors will be chain-called.
Example:
class CoopFoo:
def __init__(self, **kwargs):
super().__init__(**kwargs) # forwards all unused arguments
self.foo = 'foo'
class CoopBar:
def __init__(self, bar, **kwargs):
super().__init__(**kwargs) # forwards all unused arguments
self.bar = bar
class CoopFooBar(CoopFoo, CoopBar):
def __init__(self, bar='bar'):
super().__init__(bar=bar) # pass all arguments on as keyword
# arguments to avoid problems with
# positional arguments and the order
# of the parent classes
In this case, the order of the parent classes doesn't matter. We might as well inherit from CoopBar first, and the code would still work the same. But that's only true because all arguments are passed as keyword arguments. Using positional arguments would make it easy to get the order of the arguments wrong, so it's customary for cooperative classes to accept only keyword arguments.
This is also an exception to the rule I mentioned earlier: Both CoopFoo and CoopBar inherit from object, but they still call super().__init__(). If they didn't, there would be no cooperative inheritance.
Bottom line: The correct implementation depends on the classes you're inheriting from.
The constructor is part of a class's public interface. If the class is designed as a mixin or for cooperative inheritance, that must be documented. If the docs don't mention anything of the sort, it's safe to assume that the class isn't designed for cooperative multiple inheritance.
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.
Either approach ("new style" or "old style") will work if you have control over the source code for A and B. Otherwise, use of an adapter class might be necessary.
Source code accessible: Correct use of "new style"
class A(object):
def __init__(self):
print("-> A")
super(A, self).__init__()
print("<- A")
class B(object):
def __init__(self):
print("-> B")
super(B, self).__init__()
print("<- B")
class C(A, B):
def __init__(self):
print("-> C")
# Use super here, instead of explicit calls to __init__
super(C, self).__init__()
print("<- C")
>>> C()
-> C
-> A
-> B
<- B
<- A
<- C
Here, method resolution order (MRO) dictates the following:
C(A, B) dictates A first, then B. MRO is C -> A -> B -> object.
super(A, self).__init__() continues along the MRO chain initiated in C.__init__ to B.__init__.
super(B, self).__init__() continues along the MRO chain initiated in C.__init__ to object.__init__.
You could say that this case is designed for multiple inheritance.
Source code accessible: Correct use of "old style"
class A(object):
def __init__(self):
print("-> A")
print("<- A")
class B(object):
def __init__(self):
print("-> B")
# Don't use super here.
print("<- B")
class C(A, B):
def __init__(self):
print("-> C")
A.__init__(self)
B.__init__(self)
print("<- C")
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C
Here, MRO does not matter, since A.__init__ and B.__init__ are called explicitly. class C(B, A): would work just as well.
Although this case is not "designed" for multiple inheritance in the new style as the previous one was, multiple inheritance is still possible.
Now, what if A and B are from a third party library - i.e., you have no control over the source code for A and B? The short answer: You must design an adapter class that implements the necessary super calls, then use an empty class to define the MRO (see Raymond Hettinger's article on super - especially the section, "How to Incorporate a Non-cooperative Class").
Third-party parents: A does not implement super; B does
class A(object):
def __init__(self):
print("-> A")
print("<- A")
class B(object):
def __init__(self):
print("-> B")
super(B, self).__init__()
print("<- B")
class Adapter(object):
def __init__(self):
print("-> C")
A.__init__(self)
super(Adapter, self).__init__()
print("<- C")
class C(Adapter, B):
pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C
Class Adapter implements super so that C can define the MRO, which comes into play when super(Adapter, self).__init__() is executed.
And what if it's the other way around?
Third-party parents: A implements super; B does not
class A(object):
def __init__(self):
print("-> A")
super(A, self).__init__()
print("<- A")
class B(object):
def __init__(self):
print("-> B")
print("<- B")
class Adapter(object):
def __init__(self):
print("-> C")
super(Adapter, self).__init__()
B.__init__(self)
print("<- C")
class C(Adapter, A):
pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C
Same pattern here, except the order of execution is switched in Adapter.__init__; super call first, then explicit call. Notice that each case with third-party parents requires a unique adapter class.
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).
Although you can handle the cases where you don't control the source code of A and B by using an adapter class, it is true that you must know how the init's of the parent classes implement super (if at all) in order to do so.
As Raymond said in his answer, a direct call to A.__init__ and B.__init__ works fine, and your code would be readable.
However, it does not use the inheritance link between C and those classes. Exploiting that link gives you more consistancy and make eventual refactorings easier and less error-prone. An example of how to do that:
class C(A, B):
def __init__(self):
print("entering c")
for base_class in C.__bases__: # (A, B)
base_class.__init__(self)
print("leaving c")
This article helps to explain cooperative multiple inheritance:
The wonders of cooperative inheritance, or using super in Python 3
It mentions the useful method mro() that shows you the method resolution order. In your second example, where you call super in A, the super call continues on in MRO. The next class in the order is B, this is why B's init is called the first time.
Here's a more technical article from the official Python site:
The Python 2.3 Method Resolution Order
If you are multiply sub-classing classes from third party libraries, then no, there is no blind approach to calling the base class __init__ methods (or any other methods) that actually works regardless of how the base classes are programmed.
super makes it possible to write classes designed to cooperatively implement methods as part of complex multiple inheritance trees which need not be known to the class author. But there's no way to use it to correctly inherit from arbitrary classes that may or may not use super.
Essentially, whether a class is designed to be sub-classed using super or with direct calls to the base class is a property which is part of the class' "public interface", and it should be documented as such. If you're using third-party libraries in the way that the library author expected and the library has reasonable documentation, it would normally tell you what you are required to do to subclass particular things. If not, then you'll have to look at the source code for the classes you're sub-classing and see what their base-class-invocation convention is. If you're combining multiple classes from one or more third-party libraries in a way that the library authors didn't expect, then it may not be possible to consistently invoke super-class methods at all; if class A is part of a hierarchy using super and class B is part of a hierarchy that doesn't use super, then neither option is guaranteed to work. You'll have to figure out a strategy that happens to work for each particular case.
I added a small utility library, supers, which makes this kind of scenario simpler to handle. It works as follows:
class A(object):
def __init__(self):
print("Entering A")
print("Leaving A")
class B(object):
def __init__(self):
print("Entering B")
super(B, self).__init__()
print("Leaving B")
class C(A, B):
def __init__(self):
print("Entering C")
supers(self).__init__()
print("Leaving C")
Output when creating C:
Entering C
Entering A
Leaving A
Entering B
Leaving B
Leaving C
Here is how I have implemented the multiple inheritance in Python 3 using super()
class A:
def __init__(self, a, b, **kwargs):
print("Class A initiallised")
self.a = a
self.b = b
super().__init__(**kwargs)
print("Class A initiallisation done")
def __str__(self):
return f"{self.a} and {self.b}"
def display_a(self):
return f"{self.a} and {self.b}"
class C:
def __init__(self, c, d, **kwargs):
print("Class C initiallised")
self.c = c
self.d = d
super().__init__(**kwargs)
print("class c initiallisation done")
def __str__(self):
return f"{self.c} and {self.d}"
def display_c(self):
return f"{self.c} and {self.d}"
class D(A,C):
def __init__(self, e, **kwargs):
print("Class D initiallised")
super().__init__(**kwargs)
self.e = e
print("Class D initiallisation done")
def __str__(self):
return f"{self.e} is e,{self.b} is b,{self.a} is a,{self.d} is d,{self.c} is c"
if __name__ == "__main__":
d = D(a=12, b=13, c=14, d=15, e=16)
print(d)
d.display_c()
d.display_a()
Here is how I have implemented the super method in Python inheritance and achieved the required solution:
class A:
def __init__(self):
print("from A")
class B:
def __init__(self):
print("from B")
class C(A, B):
def __init__(self):
A.__init__(self)
B.__init__(self)
print("from C")
c = C()
Firstly, suppose you got the MRO chain
From the lowest level subclass init method on, any class which using super() method would jump into corresponding chain position, as any class which not using super() method would jump out corresponding chain position.
It follows the MRO rule and A init is called.

I get `TypeError: __init__() takes exactly 2 arguments (1 given)` error when I use multi inherit [duplicate]

Say I have a multiple inheritance scenario:
class A(object):
# code for A here
class B(object):
# code for B here
class C(A, B):
def __init__(self):
# What's the right code to write here to ensure
# A.__init__ and B.__init__ get called?
There's two typical approaches to writing C's __init__:
(old-style) ParentClass.__init__(self)
(newer-style) super(DerivedClass, self).__init__()
However, in either case, if the parent classes (A and B) don't follow the same convention, then the code will not work correctly (some may be missed, or get called multiple times).
So what's the correct way again? It's easy to say "just be consistent, follow one or the other", but if A or B are from a 3rd party library, what then? Is there an approach that can ensure that all parent class constructors get called (and in the correct order, and only once)?
Edit: to see what I mean, if I do:
class A(object):
def __init__(self):
print("Entering A")
super(A, self).__init__()
print("Leaving A")
class B(object):
def __init__(self):
print("Entering B")
super(B, self).__init__()
print("Leaving B")
class C(A, B):
def __init__(self):
print("Entering C")
A.__init__(self)
B.__init__(self)
print("Leaving C")
Then I get:
Entering C
Entering A
Entering B
Leaving B
Leaving A
Entering B
Leaving B
Leaving C
Note that B's init gets called twice. If I do:
class A(object):
def __init__(self):
print("Entering A")
print("Leaving A")
class B(object):
def __init__(self):
print("Entering B")
super(B, self).__init__()
print("Leaving B")
class C(A, B):
def __init__(self):
print("Entering C")
super(C, self).__init__()
print("Leaving C")
Then I get:
Entering C
Entering A
Leaving A
Leaving C
Note that B's init never gets called. 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 answer to your question depends on one very important aspect: Are your base classes designed for multiple inheritance?
There are 3 different scenarios:
The base classes are unrelated, standalone classes.
If your base classes are separate entities that are capable of functioning independently and they don't know each other, they're not designed for multiple inheritance. Example:
class Foo:
def __init__(self):
self.foo = 'foo'
class Bar:
def __init__(self, bar):
self.bar = bar
Important: Notice that neither Foo nor Bar calls super().__init__()! This is why your code didn't work correctly. Because of the way diamond inheritance works in python, classes whose base class is object should not call super().__init__(). As you've noticed, doing so would break multiple inheritance because you end up calling another class's __init__ rather than object.__init__(). (Disclaimer: Avoiding super().__init__() in object-subclasses is my personal recommendation and by no means an agreed-upon consensus in the python community. Some people prefer to use super in every class, arguing that you can always write an adapter if the class doesn't behave as you expect.)
This also means that you should never write a class that inherits from object and doesn't have an __init__ method. Not defining a __init__ method at all has the same effect as calling super().__init__(). If your class inherits directly from object, make sure to add an empty constructor like so:
class Base(object):
def __init__(self):
pass
Anyway, in this situation, you will have to call each parent constructor manually. There are two ways to do this:
Without super
class FooBar(Foo, Bar):
def __init__(self, bar='bar'):
Foo.__init__(self) # explicit calls without super
Bar.__init__(self, bar)
With super
class FooBar(Foo, Bar):
def __init__(self, bar='bar'):
super().__init__() # this calls all constructors up to Foo
super(Foo, self).__init__(bar) # this calls all constructors after Foo up
# to Bar
Each of these two methods has its own advantages and disadvantages. If you use super, your class will support dependency injection. On the other hand, it's easier to make mistakes. For example if you change the order of Foo and Bar (like class FooBar(Bar, Foo)), you'd have to update the super calls to match. Without super you don't have to worry about this, and the code is much more readable.
One of the classes is a mixin.
A mixin is a class that's designed to be used with multiple inheritance. This means we don't have to call both parent constructors manually, because the mixin will automatically call the 2nd constructor for us. Since we only have to call a single constructor this time, we can do so with super to avoid having to hard-code the parent class's name.
Example:
class FooMixin:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # forwards all unused arguments
self.foo = 'foo'
class Bar:
def __init__(self, bar):
self.bar = bar
class FooBar(FooMixin, Bar):
def __init__(self, bar='bar'):
super().__init__(bar) # a single call is enough to invoke
# all parent constructors
# NOTE: `FooMixin.__init__(self, bar)` would also work, but isn't
# recommended because we don't want to hard-code the parent class.
The important details here are:
The mixin calls super().__init__() and passes through any arguments it receives.
The subclass inherits from the mixin first: class FooBar(FooMixin, Bar). If the order of the base classes is wrong, the mixin's constructor will never be called.
All base classes are designed for cooperative inheritance.
Classes designed for cooperative inheritance are a lot like mixins: They pass through all unused arguments to the next class. Like before, we just have to call super().__init__() and all parent constructors will be chain-called.
Example:
class CoopFoo:
def __init__(self, **kwargs):
super().__init__(**kwargs) # forwards all unused arguments
self.foo = 'foo'
class CoopBar:
def __init__(self, bar, **kwargs):
super().__init__(**kwargs) # forwards all unused arguments
self.bar = bar
class CoopFooBar(CoopFoo, CoopBar):
def __init__(self, bar='bar'):
super().__init__(bar=bar) # pass all arguments on as keyword
# arguments to avoid problems with
# positional arguments and the order
# of the parent classes
In this case, the order of the parent classes doesn't matter. We might as well inherit from CoopBar first, and the code would still work the same. But that's only true because all arguments are passed as keyword arguments. Using positional arguments would make it easy to get the order of the arguments wrong, so it's customary for cooperative classes to accept only keyword arguments.
This is also an exception to the rule I mentioned earlier: Both CoopFoo and CoopBar inherit from object, but they still call super().__init__(). If they didn't, there would be no cooperative inheritance.
Bottom line: The correct implementation depends on the classes you're inheriting from.
The constructor is part of a class's public interface. If the class is designed as a mixin or for cooperative inheritance, that must be documented. If the docs don't mention anything of the sort, it's safe to assume that the class isn't designed for cooperative multiple inheritance.
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.
Either approach ("new style" or "old style") will work if you have control over the source code for A and B. Otherwise, use of an adapter class might be necessary.
Source code accessible: Correct use of "new style"
class A(object):
def __init__(self):
print("-> A")
super(A, self).__init__()
print("<- A")
class B(object):
def __init__(self):
print("-> B")
super(B, self).__init__()
print("<- B")
class C(A, B):
def __init__(self):
print("-> C")
# Use super here, instead of explicit calls to __init__
super(C, self).__init__()
print("<- C")
>>> C()
-> C
-> A
-> B
<- B
<- A
<- C
Here, method resolution order (MRO) dictates the following:
C(A, B) dictates A first, then B. MRO is C -> A -> B -> object.
super(A, self).__init__() continues along the MRO chain initiated in C.__init__ to B.__init__.
super(B, self).__init__() continues along the MRO chain initiated in C.__init__ to object.__init__.
You could say that this case is designed for multiple inheritance.
Source code accessible: Correct use of "old style"
class A(object):
def __init__(self):
print("-> A")
print("<- A")
class B(object):
def __init__(self):
print("-> B")
# Don't use super here.
print("<- B")
class C(A, B):
def __init__(self):
print("-> C")
A.__init__(self)
B.__init__(self)
print("<- C")
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C
Here, MRO does not matter, since A.__init__ and B.__init__ are called explicitly. class C(B, A): would work just as well.
Although this case is not "designed" for multiple inheritance in the new style as the previous one was, multiple inheritance is still possible.
Now, what if A and B are from a third party library - i.e., you have no control over the source code for A and B? The short answer: You must design an adapter class that implements the necessary super calls, then use an empty class to define the MRO (see Raymond Hettinger's article on super - especially the section, "How to Incorporate a Non-cooperative Class").
Third-party parents: A does not implement super; B does
class A(object):
def __init__(self):
print("-> A")
print("<- A")
class B(object):
def __init__(self):
print("-> B")
super(B, self).__init__()
print("<- B")
class Adapter(object):
def __init__(self):
print("-> C")
A.__init__(self)
super(Adapter, self).__init__()
print("<- C")
class C(Adapter, B):
pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C
Class Adapter implements super so that C can define the MRO, which comes into play when super(Adapter, self).__init__() is executed.
And what if it's the other way around?
Third-party parents: A implements super; B does not
class A(object):
def __init__(self):
print("-> A")
super(A, self).__init__()
print("<- A")
class B(object):
def __init__(self):
print("-> B")
print("<- B")
class Adapter(object):
def __init__(self):
print("-> C")
super(Adapter, self).__init__()
B.__init__(self)
print("<- C")
class C(Adapter, A):
pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C
Same pattern here, except the order of execution is switched in Adapter.__init__; super call first, then explicit call. Notice that each case with third-party parents requires a unique adapter class.
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).
Although you can handle the cases where you don't control the source code of A and B by using an adapter class, it is true that you must know how the init's of the parent classes implement super (if at all) in order to do so.
As Raymond said in his answer, a direct call to A.__init__ and B.__init__ works fine, and your code would be readable.
However, it does not use the inheritance link between C and those classes. Exploiting that link gives you more consistancy and make eventual refactorings easier and less error-prone. An example of how to do that:
class C(A, B):
def __init__(self):
print("entering c")
for base_class in C.__bases__: # (A, B)
base_class.__init__(self)
print("leaving c")
This article helps to explain cooperative multiple inheritance:
The wonders of cooperative inheritance, or using super in Python 3
It mentions the useful method mro() that shows you the method resolution order. In your second example, where you call super in A, the super call continues on in MRO. The next class in the order is B, this is why B's init is called the first time.
Here's a more technical article from the official Python site:
The Python 2.3 Method Resolution Order
If you are multiply sub-classing classes from third party libraries, then no, there is no blind approach to calling the base class __init__ methods (or any other methods) that actually works regardless of how the base classes are programmed.
super makes it possible to write classes designed to cooperatively implement methods as part of complex multiple inheritance trees which need not be known to the class author. But there's no way to use it to correctly inherit from arbitrary classes that may or may not use super.
Essentially, whether a class is designed to be sub-classed using super or with direct calls to the base class is a property which is part of the class' "public interface", and it should be documented as such. If you're using third-party libraries in the way that the library author expected and the library has reasonable documentation, it would normally tell you what you are required to do to subclass particular things. If not, then you'll have to look at the source code for the classes you're sub-classing and see what their base-class-invocation convention is. If you're combining multiple classes from one or more third-party libraries in a way that the library authors didn't expect, then it may not be possible to consistently invoke super-class methods at all; if class A is part of a hierarchy using super and class B is part of a hierarchy that doesn't use super, then neither option is guaranteed to work. You'll have to figure out a strategy that happens to work for each particular case.
I added a small utility library, supers, which makes this kind of scenario simpler to handle. It works as follows:
class A(object):
def __init__(self):
print("Entering A")
print("Leaving A")
class B(object):
def __init__(self):
print("Entering B")
super(B, self).__init__()
print("Leaving B")
class C(A, B):
def __init__(self):
print("Entering C")
supers(self).__init__()
print("Leaving C")
Output when creating C:
Entering C
Entering A
Leaving A
Entering B
Leaving B
Leaving C
Here is how I have implemented the multiple inheritance in Python 3 using super()
class A:
def __init__(self, a, b, **kwargs):
print("Class A initiallised")
self.a = a
self.b = b
super().__init__(**kwargs)
print("Class A initiallisation done")
def __str__(self):
return f"{self.a} and {self.b}"
def display_a(self):
return f"{self.a} and {self.b}"
class C:
def __init__(self, c, d, **kwargs):
print("Class C initiallised")
self.c = c
self.d = d
super().__init__(**kwargs)
print("class c initiallisation done")
def __str__(self):
return f"{self.c} and {self.d}"
def display_c(self):
return f"{self.c} and {self.d}"
class D(A,C):
def __init__(self, e, **kwargs):
print("Class D initiallised")
super().__init__(**kwargs)
self.e = e
print("Class D initiallisation done")
def __str__(self):
return f"{self.e} is e,{self.b} is b,{self.a} is a,{self.d} is d,{self.c} is c"
if __name__ == "__main__":
d = D(a=12, b=13, c=14, d=15, e=16)
print(d)
d.display_c()
d.display_a()
Here is how I have implemented the super method in Python inheritance and achieved the required solution:
class A:
def __init__(self):
print("from A")
class B:
def __init__(self):
print("from B")
class C(A, B):
def __init__(self):
A.__init__(self)
B.__init__(self)
print("from C")
c = C()
Firstly, suppose you got the MRO chain
From the lowest level subclass init method on, any class which using super() method would jump into corresponding chain position, as any class which not using super() method would jump out corresponding chain position.
It follows the MRO rule and A init is called.

Using super in Python 3+ multi-inheritance to init parent classes with parameters [duplicate]

Say I have a multiple inheritance scenario:
class A(object):
# code for A here
class B(object):
# code for B here
class C(A, B):
def __init__(self):
# What's the right code to write here to ensure
# A.__init__ and B.__init__ get called?
There's two typical approaches to writing C's __init__:
(old-style) ParentClass.__init__(self)
(newer-style) super(DerivedClass, self).__init__()
However, in either case, if the parent classes (A and B) don't follow the same convention, then the code will not work correctly (some may be missed, or get called multiple times).
So what's the correct way again? It's easy to say "just be consistent, follow one or the other", but if A or B are from a 3rd party library, what then? Is there an approach that can ensure that all parent class constructors get called (and in the correct order, and only once)?
Edit: to see what I mean, if I do:
class A(object):
def __init__(self):
print("Entering A")
super(A, self).__init__()
print("Leaving A")
class B(object):
def __init__(self):
print("Entering B")
super(B, self).__init__()
print("Leaving B")
class C(A, B):
def __init__(self):
print("Entering C")
A.__init__(self)
B.__init__(self)
print("Leaving C")
Then I get:
Entering C
Entering A
Entering B
Leaving B
Leaving A
Entering B
Leaving B
Leaving C
Note that B's init gets called twice. If I do:
class A(object):
def __init__(self):
print("Entering A")
print("Leaving A")
class B(object):
def __init__(self):
print("Entering B")
super(B, self).__init__()
print("Leaving B")
class C(A, B):
def __init__(self):
print("Entering C")
super(C, self).__init__()
print("Leaving C")
Then I get:
Entering C
Entering A
Leaving A
Leaving C
Note that B's init never gets called. 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 answer to your question depends on one very important aspect: Are your base classes designed for multiple inheritance?
There are 3 different scenarios:
The base classes are unrelated, standalone classes.
If your base classes are separate entities that are capable of functioning independently and they don't know each other, they're not designed for multiple inheritance. Example:
class Foo:
def __init__(self):
self.foo = 'foo'
class Bar:
def __init__(self, bar):
self.bar = bar
Important: Notice that neither Foo nor Bar calls super().__init__()! This is why your code didn't work correctly. Because of the way diamond inheritance works in python, classes whose base class is object should not call super().__init__(). As you've noticed, doing so would break multiple inheritance because you end up calling another class's __init__ rather than object.__init__(). (Disclaimer: Avoiding super().__init__() in object-subclasses is my personal recommendation and by no means an agreed-upon consensus in the python community. Some people prefer to use super in every class, arguing that you can always write an adapter if the class doesn't behave as you expect.)
This also means that you should never write a class that inherits from object and doesn't have an __init__ method. Not defining a __init__ method at all has the same effect as calling super().__init__(). If your class inherits directly from object, make sure to add an empty constructor like so:
class Base(object):
def __init__(self):
pass
Anyway, in this situation, you will have to call each parent constructor manually. There are two ways to do this:
Without super
class FooBar(Foo, Bar):
def __init__(self, bar='bar'):
Foo.__init__(self) # explicit calls without super
Bar.__init__(self, bar)
With super
class FooBar(Foo, Bar):
def __init__(self, bar='bar'):
super().__init__() # this calls all constructors up to Foo
super(Foo, self).__init__(bar) # this calls all constructors after Foo up
# to Bar
Each of these two methods has its own advantages and disadvantages. If you use super, your class will support dependency injection. On the other hand, it's easier to make mistakes. For example if you change the order of Foo and Bar (like class FooBar(Bar, Foo)), you'd have to update the super calls to match. Without super you don't have to worry about this, and the code is much more readable.
One of the classes is a mixin.
A mixin is a class that's designed to be used with multiple inheritance. This means we don't have to call both parent constructors manually, because the mixin will automatically call the 2nd constructor for us. Since we only have to call a single constructor this time, we can do so with super to avoid having to hard-code the parent class's name.
Example:
class FooMixin:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # forwards all unused arguments
self.foo = 'foo'
class Bar:
def __init__(self, bar):
self.bar = bar
class FooBar(FooMixin, Bar):
def __init__(self, bar='bar'):
super().__init__(bar) # a single call is enough to invoke
# all parent constructors
# NOTE: `FooMixin.__init__(self, bar)` would also work, but isn't
# recommended because we don't want to hard-code the parent class.
The important details here are:
The mixin calls super().__init__() and passes through any arguments it receives.
The subclass inherits from the mixin first: class FooBar(FooMixin, Bar). If the order of the base classes is wrong, the mixin's constructor will never be called.
All base classes are designed for cooperative inheritance.
Classes designed for cooperative inheritance are a lot like mixins: They pass through all unused arguments to the next class. Like before, we just have to call super().__init__() and all parent constructors will be chain-called.
Example:
class CoopFoo:
def __init__(self, **kwargs):
super().__init__(**kwargs) # forwards all unused arguments
self.foo = 'foo'
class CoopBar:
def __init__(self, bar, **kwargs):
super().__init__(**kwargs) # forwards all unused arguments
self.bar = bar
class CoopFooBar(CoopFoo, CoopBar):
def __init__(self, bar='bar'):
super().__init__(bar=bar) # pass all arguments on as keyword
# arguments to avoid problems with
# positional arguments and the order
# of the parent classes
In this case, the order of the parent classes doesn't matter. We might as well inherit from CoopBar first, and the code would still work the same. But that's only true because all arguments are passed as keyword arguments. Using positional arguments would make it easy to get the order of the arguments wrong, so it's customary for cooperative classes to accept only keyword arguments.
This is also an exception to the rule I mentioned earlier: Both CoopFoo and CoopBar inherit from object, but they still call super().__init__(). If they didn't, there would be no cooperative inheritance.
Bottom line: The correct implementation depends on the classes you're inheriting from.
The constructor is part of a class's public interface. If the class is designed as a mixin or for cooperative inheritance, that must be documented. If the docs don't mention anything of the sort, it's safe to assume that the class isn't designed for cooperative multiple inheritance.
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.
Either approach ("new style" or "old style") will work if you have control over the source code for A and B. Otherwise, use of an adapter class might be necessary.
Source code accessible: Correct use of "new style"
class A(object):
def __init__(self):
print("-> A")
super(A, self).__init__()
print("<- A")
class B(object):
def __init__(self):
print("-> B")
super(B, self).__init__()
print("<- B")
class C(A, B):
def __init__(self):
print("-> C")
# Use super here, instead of explicit calls to __init__
super(C, self).__init__()
print("<- C")
>>> C()
-> C
-> A
-> B
<- B
<- A
<- C
Here, method resolution order (MRO) dictates the following:
C(A, B) dictates A first, then B. MRO is C -> A -> B -> object.
super(A, self).__init__() continues along the MRO chain initiated in C.__init__ to B.__init__.
super(B, self).__init__() continues along the MRO chain initiated in C.__init__ to object.__init__.
You could say that this case is designed for multiple inheritance.
Source code accessible: Correct use of "old style"
class A(object):
def __init__(self):
print("-> A")
print("<- A")
class B(object):
def __init__(self):
print("-> B")
# Don't use super here.
print("<- B")
class C(A, B):
def __init__(self):
print("-> C")
A.__init__(self)
B.__init__(self)
print("<- C")
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C
Here, MRO does not matter, since A.__init__ and B.__init__ are called explicitly. class C(B, A): would work just as well.
Although this case is not "designed" for multiple inheritance in the new style as the previous one was, multiple inheritance is still possible.
Now, what if A and B are from a third party library - i.e., you have no control over the source code for A and B? The short answer: You must design an adapter class that implements the necessary super calls, then use an empty class to define the MRO (see Raymond Hettinger's article on super - especially the section, "How to Incorporate a Non-cooperative Class").
Third-party parents: A does not implement super; B does
class A(object):
def __init__(self):
print("-> A")
print("<- A")
class B(object):
def __init__(self):
print("-> B")
super(B, self).__init__()
print("<- B")
class Adapter(object):
def __init__(self):
print("-> C")
A.__init__(self)
super(Adapter, self).__init__()
print("<- C")
class C(Adapter, B):
pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C
Class Adapter implements super so that C can define the MRO, which comes into play when super(Adapter, self).__init__() is executed.
And what if it's the other way around?
Third-party parents: A implements super; B does not
class A(object):
def __init__(self):
print("-> A")
super(A, self).__init__()
print("<- A")
class B(object):
def __init__(self):
print("-> B")
print("<- B")
class Adapter(object):
def __init__(self):
print("-> C")
super(Adapter, self).__init__()
B.__init__(self)
print("<- C")
class C(Adapter, A):
pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C
Same pattern here, except the order of execution is switched in Adapter.__init__; super call first, then explicit call. Notice that each case with third-party parents requires a unique adapter class.
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).
Although you can handle the cases where you don't control the source code of A and B by using an adapter class, it is true that you must know how the init's of the parent classes implement super (if at all) in order to do so.
As Raymond said in his answer, a direct call to A.__init__ and B.__init__ works fine, and your code would be readable.
However, it does not use the inheritance link between C and those classes. Exploiting that link gives you more consistancy and make eventual refactorings easier and less error-prone. An example of how to do that:
class C(A, B):
def __init__(self):
print("entering c")
for base_class in C.__bases__: # (A, B)
base_class.__init__(self)
print("leaving c")
This article helps to explain cooperative multiple inheritance:
The wonders of cooperative inheritance, or using super in Python 3
It mentions the useful method mro() that shows you the method resolution order. In your second example, where you call super in A, the super call continues on in MRO. The next class in the order is B, this is why B's init is called the first time.
Here's a more technical article from the official Python site:
The Python 2.3 Method Resolution Order
If you are multiply sub-classing classes from third party libraries, then no, there is no blind approach to calling the base class __init__ methods (or any other methods) that actually works regardless of how the base classes are programmed.
super makes it possible to write classes designed to cooperatively implement methods as part of complex multiple inheritance trees which need not be known to the class author. But there's no way to use it to correctly inherit from arbitrary classes that may or may not use super.
Essentially, whether a class is designed to be sub-classed using super or with direct calls to the base class is a property which is part of the class' "public interface", and it should be documented as such. If you're using third-party libraries in the way that the library author expected and the library has reasonable documentation, it would normally tell you what you are required to do to subclass particular things. If not, then you'll have to look at the source code for the classes you're sub-classing and see what their base-class-invocation convention is. If you're combining multiple classes from one or more third-party libraries in a way that the library authors didn't expect, then it may not be possible to consistently invoke super-class methods at all; if class A is part of a hierarchy using super and class B is part of a hierarchy that doesn't use super, then neither option is guaranteed to work. You'll have to figure out a strategy that happens to work for each particular case.
I added a small utility library, supers, which makes this kind of scenario simpler to handle. It works as follows:
class A(object):
def __init__(self):
print("Entering A")
print("Leaving A")
class B(object):
def __init__(self):
print("Entering B")
super(B, self).__init__()
print("Leaving B")
class C(A, B):
def __init__(self):
print("Entering C")
supers(self).__init__()
print("Leaving C")
Output when creating C:
Entering C
Entering A
Leaving A
Entering B
Leaving B
Leaving C
Here is how I have implemented the multiple inheritance in Python 3 using super()
class A:
def __init__(self, a, b, **kwargs):
print("Class A initiallised")
self.a = a
self.b = b
super().__init__(**kwargs)
print("Class A initiallisation done")
def __str__(self):
return f"{self.a} and {self.b}"
def display_a(self):
return f"{self.a} and {self.b}"
class C:
def __init__(self, c, d, **kwargs):
print("Class C initiallised")
self.c = c
self.d = d
super().__init__(**kwargs)
print("class c initiallisation done")
def __str__(self):
return f"{self.c} and {self.d}"
def display_c(self):
return f"{self.c} and {self.d}"
class D(A,C):
def __init__(self, e, **kwargs):
print("Class D initiallised")
super().__init__(**kwargs)
self.e = e
print("Class D initiallisation done")
def __str__(self):
return f"{self.e} is e,{self.b} is b,{self.a} is a,{self.d} is d,{self.c} is c"
if __name__ == "__main__":
d = D(a=12, b=13, c=14, d=15, e=16)
print(d)
d.display_c()
d.display_a()
Here is how I have implemented the super method in Python inheritance and achieved the required solution:
class A:
def __init__(self):
print("from A")
class B:
def __init__(self):
print("from B")
class C(A, B):
def __init__(self):
A.__init__(self)
B.__init__(self)
print("from C")
c = C()
Firstly, suppose you got the MRO chain
From the lowest level subclass init method on, any class which using super() method would jump into corresponding chain position, as any class which not using super() method would jump out corresponding chain position.
It follows the MRO rule and A init is called.

Python Parent/Child class method call

Python 2.7.6 on Linux.
I'm using a test class that inherits from a parent. The parent class holds a number of fields that are common to many child classes, and I need to call the parent setUp method to initialize the fields. Is calling ParentClass.setUp(self) the correct way to do this? Here's a simple example:
class RESTTest(unittest.TestCase):
def setUp(self):
self.host = host
self.port = port
self.protocol = protocol
self.context = context
class HistoryTest(RESTTest):
def setUp(self):
RESTTest.setUp(self)
self.endpoint = history_endpoint
self.url = "%s://%s:%s/%s/%s" %(self.protocol, self.host, self.port, self.context, self.endpoint)
def testMe(self):
self.assertTrue(True)
if __name__ == '__main__':
unittest.main()
Is this correct? It seems to work.
You would use super for that.
super(ChildClass, self).method(args)
class HistoryTest(RESTTest):
def setUp(self):
super(HistoryTest, self).method(args)
...
In Python 3 you may write:
class HistoryTest(RESTTest):
def setUp(self):
super().method(args)
...
which is simpler.
See this answer:
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.
Multiple inheritance
To (try to) answer the question in your comment:
How do you specify which super method you want to call?
From what I understand of the philosophy of multiple inheritance (in Python), you don't. I mean, super, along with the Method Resolution Order (MRO) should do things right and select the appropriate methods. (Yes methods is a plural, see below.)
There are a lot of blog posts / SO answers about this you can find with keywords "multiple inheritance", "diamond", "MRO", "super", etc. This article provides a Python 3 example I found surprising and didn't find in other sources:
class A:
def m(self):
print("m of A called")
class B(A):
def m(self):
print("m of B called")
super().m()
class C(A):
def m(self):
print("m of C called")
super().m()
class D(B,C):
def m(self):
print("m of D called")
super().m()
D().m()
m of D called
m of B called
m of C called
m of A called
See? Both B.m() and C.m() are called thanks to super, which seems like the right thing to do considering D inherits from both B and C.
I suggest you play with this example like I just did. Adding a few prints, you'll see that, when calling D().m(), the super().m() statement in class B itself calls C.m(). Whereas, of course, if you call B().m() (B instance, not D instance), only A.m() is called. In other words, super().m() in B is aware of the class of the instance it is dealing with and behaves accordingly.
Using super everywhere sounds like the silver bullet, but you need to make sure all classes in the inheritance schema are cooperative (another keyword to dig for) and don't break the chain, for instance when expecting additional parameters in child classes.

Categories

Resources