Python Object-Oriented Python Inheritance Multiple Superclasses

Tyler Wells
Tyler Wells
Python Web Development Techdegree Student 2,677 Points

MRO in superclasses/sibling classes and super()

So the MRO first and foremost reads left to right (goes through Agile, Sneaky then Character classes)?

Then by doing that, and through using super() in the __init__ we're then sending from the Agile class the attribute that the thief is agile to the Sneaky class which then sends both agile and sneaky attributes to the character class? Thats the jist of super()? But I thought in order for super to work we had to have the exact same function/method in the subclass as the superclass (taking out the self parameter), no?

Jay Reyes
Jay Reyes
Python Web Development Techdegree Student 15,935 Points

I'm confused as well...

class Thief(Agile, Sneaky, Character):

From the above: 1) why is super() in sneaky and agile __init__ when both are the super class of thief?

2) Does character become the parent class for sneaky, agile, and thief because it is "final"?

Like you seem to say ( tell me if otherwise), I expect ...

class Thief(Agile, Sneaky, Character):
    def pickpocket(self):
        return self.sneaky and bool(random.randint(0, 1)

... to contain an __init__ that contains super()

1 Answer

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 59,027 Points

Great questions!

From Tyler:

The MRO first and foremost reads left to right (goes through Agile, Sneaky then Character classes. Correct!

Then by doing that, and through using super() in the __init__ we're then sending from the Agile class the attribute that the thief is agile to the Sneaky class which then sends both agile and sneaky attributes to the character class? Almost correct. The beauty of using *args and **kwargs is they only get assigned the “leftover” attributes. Since Agile defines agile it is not passed on to Sneaky and, since Sneaky defines sneaky it is not passed on to Character.

That’s the jist of super()? think of super() as a temporary redirect to a method of the same name somewhere in the MRO. The first method with the same name is run. It’s only by including additional super() calls in subsequently reached methods that any chaining occurs. Otherwise, execution would stop at the first method without a super().

But I thought in order for super to work we had to have the exact same function/method in the subclass as the superclass (taking out the self parameter)? Correct. If super().__some_method__() is called, then the method __some_method__ must exist somewhere on the MRO or an AttributeError will be raised.

From Jay:

why is super() in sneaky and agile init when both are the super class of thief? Execution will stop at the first parent method that does not contain super().

Does character become the parent class for sneaky, agile, and thief because it is "final"? Sneaky and Agile only have the base object class as a parent. The three parent classes are better described as ordered co-parent classes.

I expect [method Thief.pickpocket] to contain an init that contains super(). the __init__ methods are only called at the initialization of the class instance. The method pickpocket would only contain a super() call if:

  • a method of the same name existed in one of the parent classes listed in the MRO, (or a parent of a parent, etc.), and
  • you wanted the parent version of the method to also be run.

Food food thought: The super() call can be the first line, a middle line, or the last of a method depending on when you want the parent code to run. Putting super() first allows you to modify the parent’s actions, putting super() last allows you to modify parameters before they’re sent to the parent, putting in the middle of a method allow you to do both.

Post back if you need more help. Good luck!!!

Anthony Grodowski
Anthony Grodowski
4,899 Points

Hi Chris!

Could you please explain to me what is chaining?

I also don't fully get this sentence - "It’s only by including additional super() calls in subsequently reached methods that any chaining occurs. Otherwise, execution would stop at the first method without a super()."

"The beauty of using *args and **kwargs is they only get assigned the “leftover” attributes. Since Agile defines agile it is not passed on to Sneaky and, since Sneaky defines sneaky it is not passed on to Character." - so does it mean that after the execution Agile Class will also have sneaky attribute and Character Class will have sneaky and agile attribute?

"Sneaky and Agile only have the base object class as a parent." - and lastly - what object is that and why do we call it (I've seen your previous answer on a similar topic in which you said that it's a do-nothing class - so why do we use it? To be able to use super to continue chaining?) ?

I'd be very grateful for you answer! Cheers, Antek!

Chris Freeman
Chris Freeman
Treehouse Moderator 59,027 Points

Could you please explain to me what is chaining? “Chaining” is not an actual term, but a metaphor I use to mean flowing execution through each of the parent classes in turn.

I also don't fully get this sentence - "It’s only by including additional super() calls in subsequently reached methods that any chaining occurs. Otherwise, execution would stop at the first method without a super()." super() says only run the method from the first parent (or the next parent in the MRO). Execution stops there unless another super is encountered to keep execution going to the next parent on the MRO.

"The beauty of using *args and **kwargs is they only get assigned the “leftover” attributes. Since Agile defines agile it is not passed on to Sneaky and, since Sneaky defines sneaky it is not passed on to Character." - so does it mean that after the execution Agile Class will also have sneaky attribute and Character Class will have sneaky and agile attribute? The base classes Agile will have agile, Sneaky will have Sneaky, but Character will have neither. Only the instantiation of Thief will have all the attributes. Keep in mind that the executing code is from the parent classes but is being executed in the Thief instance.

"Sneaky and Agile only have the base object class as a parent." - and lastly - what object is that and why do we call it (I've seen your previous answer on a similar topic in which you said that it's a do-nothing class - so why do we use it? To be able to use super to continue chaining?) ? The base class object is inherented from automatically.

It has the methods that are common to all instances of Python classes.

In the cases of multiple inheritance where every parent inherits from object, the MRO is dynamically structured such that object occurs exactly once and is always the last in the order.

You can see the MRO of Thief using:

print(Thief().__class__.__mro__)

I'd be very grateful for you answer! Cheers, Antek! You are very welcome!

Anthony Grodowski
Anthony Grodowski
4,899 Points

Thanks! Answer professional as always:)! So does it mean that as long as Sneaky and Agile don't have methods that depend on each other (for example in Sneaky there's a method that is neccessary for some Agile method to run) and both have super() and Character doesn't, it doesn't matter if it's Thief(Agile, Sneaky, Character) or Thief(Sneaky, Agile, Character).

Also I'd like to make sure that I got super() right. So basically it's a redirect from a Child class to a Parent class, which can be 'chained'. For example in Thief(Agile, Sneaky, Character) first of all, ALL of the Agile mehods are being run, then super() in Agile redirects to Sneaky in which the code BELOW super() which is indented in super() code block is being run (in this case self.sneaky = sneaky) and Sneaky redirects to Character, which doesn't contain super(), so the execution stops after WHOLE Character code is being executed.

If I'm right, super() has 2 fuctions - one is to redirect to an another class and the second is to overwrite a piece of code from the child class by a piece of code of the parent class code Did I get all of that right?

Chris Freeman
Chris Freeman
Treehouse Moderator 59,027 Points

... it doesn't matter if it's Thief(Agile, Sneaky, Character) or Thief(Sneaky, Agile, Character). correct!

For example in Thief(Agile, Sneaky, Character) first of all, ALL of the Agile mehods are being run, then super() in Agile redirects to Sneaky in which the code BELOW super() which is indented in super() code block is being run (in this case self.sneaky = sneaky) and Sneaky redirects to Character, which doesn't contain super(), so the execution stops after WHOLE Character code is being executed. This is close. Not all methods in Agile run. Only the method called and it’s equivalent called by super() are run. once the super() call is complete, the execution returns to the call method. For __init__ the flow goes:

  • Thief.__Init__ pre-super -> Agile.__init__ pre-super -> Sneaky.__init__ pre-super -> Character.__init__ -> Sneaky.__init__ post-super -> Agile.__init__ post-super -> Thief.__init__ post-super

If I'm right, super() has 2 fuctions - one is to redirect to an another class yes! and the second is to overwrite a piece of code from the child class by a piece of code of the parent class code the overriding is more the child is overriding the parent’s code. An override I’d not always necessary. The parent’s code might only be augmented or extended.