0

This code shows error: AttributeError: 'super' object has no attribute 'method'

this same code does not shows error when "super.method()" is removed in class C. Why?

class A(object):
    def method(self):
        print("\n class A method")
        super().method()

class B(object):
    def method(self):
        print("\n class B method")
        super().method()

class C(object):
    def method(self):
        print("\n class C method")
        super().method()
        

class X(A, B):
    def method(self):
        print("\n class X method")
        super().method()

class Y(B, C):
    def method(self):
        print("\n class Y method")
        super().method()

class P(X, Y, C):
    def method(self):
        print("\n class P method")
        super().method()

p = P()
p.method()

print(P.mro())

I want to know, why this program is showing errors when "super().method" is included in class C, and why this same program is NOT SHOWING ERROR, when "super().method" is removed in class C?

1 Answers1

1

This is not a bug, but a feature of working with multiple inheritance and super. Let me answer your questions one by one:

why this program is showing errors when "super().method" is included in class C?

The reason is that the base class object does not have a method called method. You will see the same exception when calling C().method() or simply object().method(). So I guess this is quite expected.

why this same program is NOT SHOWING ERROR, when "super().method" is removed in class C?

This is the magic of python's method resolution order (MRO). If you execute

print(P.mro())

you should see

[__main__.P,
 __main__.X,
 __main__.A,
 __main__.Y,
 __main__.B,
 __main__.C,
 object]

Here, you see that a super call will not call all parents of a class until the top of the inheritance tree and then take the next class. Instead, it will call them according to their "generation".

Look at the following example:

class Base:
    def method(self):
        print("Base")

class ParentA(Base):
    def method(self):
        print('Parent A')
        super().method()
        
class ParentB(Base):
    def method(self):
        print("Parent B")
        
class Child(ParentA, ParentB):
    def method(self):
        print("Child")
        super().method()

print(Child.mro())
Child().method()

Which will give you

[<class '__main__.Child'>, <class '__main__.ParentA'>, <class '__main__.ParentB'>, <class '__main__.Base'>, <class 'object'>]
Child
Parent A
Parent B

You do not see the line Base, because super in ParentA is ignored due to the presence of ParentB which shares the same base.

However, if you swap the inheritance, you will see the line Base printed. In short, follow the MRO, but if the last class in a generation does not call super, the chain stops.

I agree that this is not intuitive, but in practice this is rarely a problem, or if you have to think about the MRO to understand the code, it would be a good candidate for refactoring.

To understand the MRO, I recommend to read https://en.wikipedia.org/wiki/C3_linearization or other related questions at SO.

Carlos Horn
  • 1,115
  • 4
  • 17
  • thank you for explaining. I have one more doubt. in this above program OBJECT class DOES NOT have "method" method, that's why it was showing error. BUT, lets say in place of "method" we are using CONSTRUCTORS in each class, and when "super().__init()" will be included in C class, The MRO will visit or check the OBJECT class at last after visiting class C as second last class. but it will not show error. It will show the proper MRO path followed. MY QUESTION IS why the same program is showing error in "method" method but will not show error when "method" is replaced by individual constructor? – Sandeep Chetia Jan 12 '23 at 05:19
  • 1
    I would suggest to read this answer https://stackoverflow.com/a/73744147/18205911 which explains the same problem for the case of the init. – Carlos Horn Jan 12 '23 at 08:17