Python multi-inheritance, __init__

22,373

Solution 1

If you want to use super in child to call parent.__init__ and parent2._init__, then both parent __init__s must also call super:

class parent(Base):
    def __init__(self,x=1,y=2):
        super(parent,self).__init__(x,y)   

class parent2(Base):
    def __init__(self,x=3,y=4):
        super(parent2,self).__init__(x,y)

See "Python super method and calling alternatives" for more details on the sequence of calls to __init__ caused by using super.


class Base(object): 
    def __init__(self,*args):
        pass

class parent(Base):
    var1=1
    var2=2
    def __init__(self,x=1,y=2):
        super(parent,self).__init__(x,y)        
        self.var1=x
        self.var2=y

class parent2(Base):
    var4=11
    var5=12
    def __init__(self,x=3,y=4):
        super(parent2,self).__init__(x,y)
        self.var4=x
        self.var5=y

    def parprint(self):
        print self.var4
        print self.var5

class child(parent, parent2):
    var3=5
    def __init__(self,x,y):
        super(child, self).__init__(x,y)


childobject = child(9,10)
print childobject.var1
print childobject.var2
print childobject.var3
childobject.parprint()

You might be wondering, "Why use Base?". If parent and parent2 had inherited directly from object, then super(parent2,self).__init__(x,y) would call object.__init__(x,y). That raises a TypeError since object.__init__() takes no parameters.

To workaround this issue, you can make a class Base which accepts arguments to __init__ but does not pass them on to object.__init__. With parent and parent2 inheriting from Base, you avoid the TypeError.

Solution 2

Because parent is next in method resolution order (MRO), and it never uses super() to call into parent2.

Solution 3

See this example:

class Base(object): 
    def __init__(self, c):
        print('Base called by {0}'.format(c))
        super().__init__()

class ParentA(Base):
    def __init__(self, c):
        print('ParentA called by {0}'.format(c))
        super().__init__('ParentA')

class ParentB(Base):
    def __init__(self, c):
        print('ParentB called by {0}'.format(c))
        super().__init__('ParentB')

class Child(ParentA, ParentB):
    def __init__(self, c):
        print('Child called by {0}'.format(c))
        super().__init__('Child')

Child('Construct')
print(Child.mro())

This will output:

Child called by Construct
ParentA called by Child
ParentB called by ParentA
Base called by ParentB
[<class '__main__.Child'>, <class '__main__.ParentA'>, <class '__main__.ParentB'>, <class '__main__.Base'>, <class 'object'>]

Python multiple inheritance is like a chain, in Child class mro, the super class of ParentA is ParentB, so you need call super().__init__() in ParentA to init ParentB.

If you change super().__init__('ParentA') to Base.__init__(self, 'ParentA'), this will break the inheritance chain, output:

Child called by Construct
ParentA called by Child
Base called by ParentA
[<class '__main__.Child'>, <class '__main__.ParentA'>, <class '__main__.ParentB'>, <class '__main__.Base'>, <class 'object'>]

More info about MRO

Share:
22,373
Renl
Author by

Renl

Updated on July 05, 2022

Comments

  • Renl
    Renl almost 2 years

    Regarding multiple parent inheritance, when I call the super.__init__, why doesn't parent2's __init__ function get called? Thanks.

    class parent(object):
        var1=1
        var2=2
        def __init__(self,x=1,y=2):
            self.var1=x
            self.var2=y
    
    class parent2(object):
        var4=11
        var5=12
        def __init__(self,x=3,y=4):
            self.var4=x
            self.var5=y
    
        def parprint(self):
            print self.var4
            print self.var5
    
    class child(parent, parent2):
        var3=5
        def __init__(self,x,y):
            super(child, self).__init__(x,y)
    
    childobject = child(9,10)
    print childobject.var1
    print childobject.var2
    print childobject.var3
    childobject.parprint()
    

    Output is

    9
    10
    5
    11
    12