Search code examples
pythonclassmethod-resolution-order

How would the super() affect the order in MRO?


ALL sample codes from JetBrains academy.

If i execute a code like this,

#Code sample 1
class Robot:
    def greet(self):
        print("I am a robot")


class Android(Robot):
    def greet(self):
        super().greet()
        print("I am an android")


class PersonalAssistant(Robot):
    def greet(self):
        super().greet()
        print("I am a personal assistant")


class AssistantAndroid(Android, PersonalAssistant):
    def greet(self):
        super().greet()

The output is

I am a robot
I am a personal assistant
I am an android
None

Why the personal assistant is printed before the android? The MRO of AssistantAndroid is (<class '__main__.AssistantAndroid'>, <class '__main__.Android'>, <class '__main__.PersonalAssistant'>, <class '__main__.Robot'>, <class 'object'>) So first we go to the Android and first found super in Android which make us go to robot then why the code goes to PersonalAssistant instead of execute the next print(I am an android) in Android?

Moreover, could someone teach me where does the None comes from?

If the super moved into the next line of print() in the subclass like this.

#Code sample 2
class Person:
    def print_message(self):
        print("Message from Person")


class Student(Person):
    def print_message(self):
        print("Message from Student")
        super().print_message()


class Programmer(Person):
    def print_message(self):
        print("Message from Programmer")
        super().print_message()


class StudentProgrammer(Student, Programmer):
    def print_message(self):
        super().print_message()

The the output looks in correct order as MRO.

jack = StudentProgrammer()
jack.print_message()
# Message from Student
# Message from Programmer
# Message from Person

But why the #Code sample 1 execute order so strange?

----------

I have added some additional prints here and things are more weird to me..

class Robot:
    def greet(self):
        print("Start robot")
        print("I am a robot")
        print("Finish robot")


class Android(Robot):
    def greet(self):
        print("Start Android")
        super().greet()
        print("I am an android")
        print("Finish Android")


class PersonalAssistant(Robot):
    def greet(self):
        print("Start PersonalAssistant")
        super().greet()
        print("I am a personal assistant")
        print("Finish PersonalAssistant")


class AssistantAndroid(Android, PersonalAssistant):
    def greet(self):
        print("Start AssistantAndroid")
        super().greet()
        print("Finish AssistantAndroid")


a = AssistantAndroid()
print('AssistantAndroid', AssistantAndroid.__mro__)
print('Android', Android.__mro__)
print('PersonalAssistant', PersonalAssistant.__mro__)
print("--------------------")
print(a.greet())

The output is

Start AssistantAndroid
Start Android
Start PersonalAssistant
Start robot
I am a robot
Finish robot
I am a personal assistant
Finish PersonalAssistant
I am an android
Finish Android
Finish AssistantAndroid
None

Does this means, when the execute goes to the level 2 classes( Android and PersonalAssistant), it only execute one line in Android and then one line in PersonalAssistant, then when it executes the super() in Android?? and it means execute the robot and then after finish the robot, it goes to second line in PersonalAssistant which is also means robot, then it skipped it??, then why finish PersonalAssistant and then finish the Android...I am totally confused..


Very appreciate to the answer from @Blckknght

Please refer to his answer I think it explains very well. Basically, when the Multiple inheritance comes, the only relation you should follow is the MRO order. (The reverse of MRO) So this is why after the Start Android follows Start PersonalAssistant but not Start robot because from the reverse of MRO, the PersonalAssistant becomes the father of Android. So the super() in Android will call the the PersonalAssistant but not robot. Correct me if my understanding is wrong.


Solution

  • When all of your greet methods call super().greet before printing their own text, the output will come in the reverse of the MRO (excluding object, since it doesn't have a greet method).

    Think about the call stack, where each method waits for the super().greet call to return before going on to print. Here's what it looks like, with indentation indicating the depth of the stack:

    # in AssistantAndroid.greet()...
    super().greet() # resolves to Android.greet() when self is an AssistantAndroid
    
        # in Android.greet()...
        super().greet() # resolves to PersonalAssitant.greet() when self is an AssistantAndroid
    
            # in PersonalAssistant.greet()...
            super().greet() # resolves to Robot.greet() when self is an AssistantAndroid
    
                # in Robot.greet(), where there's no super() call
                print("I am a robot")
    
            # continuing PersonalAssistant.greet()
            print("I am a personal assistant")
    
        # continuing Android.greet()
        print("I am an android")
    
    # continuing AssistantAndroid.greet(), but there's nothing left to do