Reza F
Reza F

Reputation: 25

Multiple inheritance difference between Python 2.7 and 3

I've already got that there is a difference between Python 2.7 and 3. implementing multiple inheritance, for example:

In Python 3.:

class A:
    def __init__(self, x2='', x3='', **kwargs):
        print kwargs
        super().__init__(**kwargs)
        self.x2 = x2
        self.x3 = x3

class B:
    def __init__(self, x4='', x5='', **kwargs):
        print kwargs
        super().__init__(**kwargs)
        self.x4 = x4
        self.x5 = x5

class C(A, B):
    def __init__(self, x1='', **kwargs):
        print kwargs
        super().__init__(**kwargs)
        self.x1 = x1

And in Python 2.7:

class A(object):
    def __init__(self, x2='', x3='', **kwargs):
        print kwargs
        super(A, self).__init__(**kwargs)
        self.x2 = x2
        self.x3 = x3

class B(object):
    def __init__(self, x4='', x5='', **kwargs):
        print kwargs
        super(B, self).__init__(**kwargs)
        self.x4 = x4
        self.x5 = x5

class C(A, B):
    def __init__(self, x1='', **kwargs):
        print kwargs
        super(C, self).__init__(**kwargs)
        self.x1 = x1

Running the code:

C(x1='a',x2='b',x3='c',x4='d',x5='e',x6='f',x7='g')

In Python 3., I got:

{'x2': 'b', 'x3': 'c', 'x6': 'f', 'x7': 'g', 'x4': 'd', 'x5': 'e'}
{'x6': 'f', 'x7': 'g', 'x4': 'd', 'x5': 'e'}
{'x6': 'f', 'x7': 'g'}

But doing the same, Python 2.76 would result in an error too:

{'x2': 'b', 'x3': 'c', 'x6': 'f', 'x7': 'g', 'x4': 'd', 'x5': 'e'}
{'x6': 'f', 'x7': 'g', 'x4': 'd', 'x5': 'e'}
{'x6': 'f', 'x7': 'g'}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
super(C, self).__init__(**kwargs)
super(A, self).__init__(**kwargs)
super(B, self).__init__(**kwargs)
TypeError: object.__init__() takes no parameters

I noticed if I remove super(B, self).__init__(**kwargs) from Python 2.7 code, there won't be any error, as probably class B does not inherit from class A. Anybody knows how this work without any problem in Python 3? and any solution solving this problem in Python 2.7?

Upvotes: 1

Views: 1260

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1123420

Use a sentinel object at the bottom of your inheritance tree that does not call super().__init__():

class Sentinel(object):
    def __init__(self, **kw):
        pass

class A(Sentinel):
    def __init__(self, x2='', x3='', **kwargs):
        super(A, self).__init__(**kwargs)
        self.x2 = x2
        self.x3 = x3

class B(Sentinel):
    def __init__(self, x4='', x5='', **kwargs):
        super(B, self).__init__(**kwargs)
        self.x4 = x4
        self.x5 = x5

class C(A, B):
    def __init__(self, x1='', **kwargs):
        super(C, self).__init__(**kwargs)
        self.x1 = x1

That way Sentinel.__init__ is always called last and not object.__init__. This way you ensure that all your __init__ methods accept the same arguments, always.

See Raymond Hettinger's super() considered super! article (or the PyCon presentation based on it).

Upvotes: 1

Related Questions