Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple inheritance in python3 with different signatures

I have three classes: A, B and C.

C inherits from A and B (in this order). The constructor signatures of A and B are different. How can I call the __init__ methods of both parent classes?

My endeavour in code:

class A(object):     def __init__(self, a, b):         super(A, self).__init__()         print('Init {} with arguments {}'.format(self.__class__.__name__, (a, b)))  class B(object):     def __init__(self, q):         super(B, self).__init__()         print('Init {} with arguments {}'.format(self.__class__.__name__, (q)))  class C(A, B):     def __init__(self):         super(A, self).__init__(1, 2)         super(B, self).__init__(3)  c = C() 

yields the error:

Traceback (most recent call last):   File "test.py", line 16, in <module>     c = C()   File "test.py", line 13, in __init__     super(A, self).__init__(1, 2) TypeError: __init__() takes 2 positional arguments but 3 were given 

I found this resource which explains mutiple inheritance with different set of arguments, but they suggest to use *args and **kwargs to use for all argument. I consider this very ugly, since I cannot see from the constructor call in the child class what kind of parameters I pass to the parent classes.

like image 389
Nikolai Tschacher Avatar asked Nov 14 '14 10:11

Nikolai Tschacher


People also ask

Is multiple inheritance Pythonic?

A class can be derived from more than one base class in Python, similar to C++. This is called multiple inheritance. In multiple inheritance, the features of all the base classes are inherited into the derived class.

Can a subclass have two superclasses Python?

In Python a class can inherit from more than one class. If a class inherits, it has the methods and variables from the parent classes. In essence, it's called multiple inheritance because a class can inherit from multiple classes.

Why multiple inheritance is not supported in Python?

One problem occurs when two parent classes have data members or methods of the same name. It is difficult to resolve which is being referenced by the sub-class. Another occurs when two parent classes inherit from the same base class, forming a "diamond" pattern in the inheritance hierarchy.

Why multiple inheritance is not a good idea?

Allowing multiple inheritance makes the rules about function overloads and virtual dispatch decidedly more tricky, as well as the language implementation around object layouts. These impact language designers/implementors quite a bit and raise the already high bar to get a language done, stable, and adopted.


1 Answers

Do not use super(baseclass, ...) unless you know what you are doing. The first argument to super() tells it what class to skip when looking for the next method to use. E.g. super(A, ...) will look at the MRO, find A, then start looking for __init__ on the next baseclass, not A itself. For C, the MRO is (C, A, B, object), so super(A, self).__init__ will find B.__init__.

For these cases, you don't want to use cooperative inheritance but directly reference A.__init__ and B.__init__ instead. super() should only be used if the methods you are calling have the same signature or will swallow unsupported arguments with *args and **vargs. In that case just the one super(C, self).__init__() call would be needed and the next class in the MRO order would take care of chaining on the call.

Putting it differently: when you use super(), you can not know what class will be next in the MRO, so that class better support the arguments you pass to it. If that isn't the case, do not use super().

Calling the base __init__ methods directly:

class A(object):     def __init__(self, a, b):         print('Init {} with arguments {}'.format(self.__class__.__name__, (a, b)))  class B(object):     def __init__(self, q):         print('Init {} with arguments {}'.format(self.__class__.__name__, (q)))  class C(A, B):     def __init__(self):         # Unbound functions, so pass in self explicitly         A.__init__(self, 1, 2)         B.__init__(self, 3) 

Using cooperative super():

class A(object):     def __init__(self, a=None, b=None, *args, **kwargs):         super().__init__(*args, **kwargs)         print('Init {} with arguments {}'.format(self.__class__.__name__, (a, b)))  class B(object):     def __init__(self, q=None, *args, **kwargs):         super().__init__(*args, **kwargs)         print('Init {} with arguments {}'.format(self.__class__.__name__, (q)))  class C(A, B):     def __init__(self):         super().__init__(a=1, b=2, q=3) 
like image 142
Martijn Pieters Avatar answered Sep 28 '22 04:09

Martijn Pieters