Is super in python redundant?

Viewed 107

In the following example I get the same output if I replace super in CL2 with self.test() which is " in cl2 test cl5". How exactly does super make any difference.

class CL1(object):
    def test(self):
        print "test cl1"

class CL2(CL1):
    def abc(self):

        print "in cl2"
        super(CL2,self).test()
        #self.test()

class CL3(CL1):
    def test(self):
        print "test cl3"

class CL5(CL1):
    def test(self):
        print "test cl5"

class CL4(CL2,CL5,CL3):
    def add(self):
        print 'cl4'

def main()
   o=CL4()
   o.abc()
if __name__ == "__main__":

    main()
2 Answers

If you're not using super, then inside the class CL2, it will call its parent(CL1)'s test() method implicitly.

But the ambiguous problem occurs when class CL2 also define a method named test() (this is called method overriding in OOP). The default is using the current class's test() method, but if you want to call the parent's one instead, then it's when you need to use super to call its super class's test() method explicitly.

Consider this illustration:

class CL1(object):
    def test(self):
        print "test CL1"

class CL2(CL1):
    def test(self):
      print "test CL2"

    def abc(self):

        print "in CL2"
        super(CL2,self).test()
        self.test()

o=CL2()
o.abc()

Will output:

in CL2
test CL1
test CL2

super is not just a convenience for leaving the name of the base class out of your child-class methods; it is designed for cooperative inheritance, in which properly-designed classes will call overriden methods in the correct order according to the C3 linearization algorithm.

super does not necessarily refer to the statically declared parent of the class you are defining. Consider the following code:

class A:
    def foo(self):
        print("A.foo")

class B(A):
    def foo(self):
        print("B before super")
        super().foo()
        print("B.foo")

class C(A):
    def foo(self):
        print("C before super")
        super().foo()
        print("C.foo")

class D(B, C):
    def foo(self):
        print("D before super")
        super().foo()
        print("D.foo")

print(D.__mro__)
d = D()
d.foo()

The call super().foo() inside B.foo does not call A.foo when you run d.foo(); it runs C.foo instead, because C comes immediately after B in the method resolution order (MRO) of class D.

If you are not OK with B.foo not always calling A.foo immediately, regardless of the type of self, don't use super. By using super, you are agreeing to the semantics it was designed to support, and should document the method as using super. Otherwise, just use A.foo(self) instead, and document the fact that you are not using super.

Related