9

This is an extension to this question and raises a problem, which you, my fellow StackOverflowers, will hopefully be able to help me with. From the referenced question, consider the final code sample:

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"

As noted by the poster, when initialising C, the __init__ for B is never called. On considering Raymond Hettinger's post, the code for class A should be modified to also call super().__init__():

class A(object):
    def __init__(self):
        print "entering A"
        super(A, self).__init__()
        print "leaving A"

We're all good so far. However, what if our class' __init__ functions accept arguments? Let's assume that all the __init__ functions accept a single argument and for consistency, we'll simply call it foo, the code is now:

class A(object):
    def __init__(self, foo):
        print "entering A"
        super(A, self).__init__(foo)
        print "leaving A"

class B(object):
    def __init__(self, foo):
        print "entering B"
        super(B, self).__init__(foo)
        print "leaving B"

class C(A,B):
    def __init__(self, foo):
        print "entering c"
        super(C, self).__init__(foo)
        print "leaving c"

Here we hit a snag. When initialising any of the classes A, B or C, we eventually call object.__init__ with a single argument, foo, which errors with TypeError: object.__init__() takes no parameters. However, to remove one of the super().__init__ functions would mean the classes are no longer cooperative in the case of multiple inheritance.

After all that, my question is how does one get around this problem? It appears that multiple inheritance is broken in anything but cases where no arguments are passed to the __init__ functions.

UPDATE:

Rob in the comments suggests stripping keyword arguments (referenced in Raymond H's post). This actually works very well in the case of multiple inheritance until your change you code. If one of your functions no longer uses one of the keyword arguments and ceases to strip it without modifying the calling function, You will still receive the TypeError noted above. As such, this seems like a fragile solution for large projects.

Community
  • 1
  • 1
Endophage
  • 21,038
  • 13
  • 59
  • 90
  • 2
    FWIW, "Good design dictates that this method have the same calling signature in every case" -- http://docs.python.org/2/library/functions.html#super – Robᵩ Apr 30 '13 at 17:10
  • 3
    Also, see http://rhettinger.wordpress.com/2011/05/26/super-considered-super/, especially the bit about using keyword arguments for `.__init__()`. – Robᵩ Apr 30 '13 at 17:12
  • 1
    @Robᵩ understood, but I don't control the signature of `object.__init__` and it's pretty damn normal to have class `__init__` functions accept arguments. It seems that `object.__init__` should have been defined as `__init__(*args, **kwargs)` and then just ignore anything passed in. – Endophage Apr 30 '13 at 17:12
  • @Robᵩ I already referenced that Raymond Hettinger post. Stripping keywords might help... I'll have to consider that some more. – Endophage Apr 30 '13 at 17:14

2 Answers2

2

Admittedly, this solution may not be the most pythonic or ideal, but creating a wrapper class for object like so allows you to pass arguments around each __init__ in the inheritance:

class Object(object):
    def __init__(self,*args,**kwargs):
        super(Object,self).__init__()

class A(Object):
    def __init__(self,*args,**kwargs):
        super(A,self).__init__(*args,**kwargs)

class B(Object):
    def __init__(self,*args,**kwargs):
        super(B,self).__init__(*args,**kwargs)

class C(A,B):
    def __init__(self,*args,**kwargs):
        super(C,self).__init__(*args,**kwargs)
ievans3024
  • 21
  • 2
2

My answer might be a little off. I've been looking for code to solve my particular problem. But after searching for hours I could not find good example. So I wrote this little test code. I think it is definitely cooperative multiple inheritance example. I really think someone might find it useful. So here we go!

Basically, I had a pretty big class that I wanted to split even further but due to my particular case it had to be the same class. Also all of my child classes had their own inits that I wanted to executed after base init so to speak.

Test Code

"""
Testing MRO Functionality of python 2.6 (2.7)
"""


class Base(object):
    def __init__(self, base_arg, **kwargs):
        print "Base Init with arg: ", str(base_arg)
        super(Base, self).__init__()

    def base_method1(self):
        print "Base Method 1"

    def base_method2(self):
        print "Base Method 2"


class ChildA(Base):
    def __init__(self, child_a_arg, **kwargs):
        super(ChildA, self).__init__(**kwargs)
        print "Child A init with arg: ", str(child_a_arg)

    def base_method1(self):
        print "Base Method 1 overwritten by Child A"


class ChildB(Base):
    def __init__(self, child_b_arg, **kwargs):
        super(ChildB, self).__init__(**kwargs)
        print "Child B init with arg: ", str(child_b_arg)

    def base_method2(self):
        print "Base Method 2 overwritten by Child B"


class ChildC(Base):
    def __init__(self, child_c_arg, **kwargs):
        super(ChildC, self).__init__(**kwargs)
        print "Child C init with arg: ", str(child_c_arg)

    def base_method2(self):
        print "Base Method 2 overwritten by Child C"


class Composite(ChildA, ChildB, ChildC):
    def __init__(self):
        super(Composite, self).__init__(base_arg=1, child_a_arg=2, child_b_arg=3, child_c_arg=4)
        print "Congrats! Init is complete!"


if __name__ == '__main__':
    print "MRO: ", str(Composite.__mro__), "\n"
    print "*** Init Test ***"
    test = Composite()
    print "*** Base Method 1 Test ***"
    test.base_method1()
    print "*** Base Method 2 Test ***"
    test.base_method2()

Output

MRO:  (<class '__main__.Composite'>, 
       <class '__main__.ChildA'>, 
       <class '__main__.ChildB'>, 
       <class '__main__.ChildC'>, 
       <class '__main__.Base'>, 
       <type 'object'>)

*** Init Test ***
Base Init with arg:  1
Child C init with arg:  4
Child B init with arg:  3
Child A init with arg:  2
Congrats! Init is complete!
*** Base Method 1 Test ***
Base Method 1 overwritten by Child A
*** Base Method 2 Test ***
Base Method 2 overwritten by Child B
martineau
  • 119,623
  • 25
  • 170
  • 301
Barmaley
  • 1,232
  • 20
  • 27