Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community!

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python Object-Oriented Python Inheritance Multiple Superclasses

Michal Janek
.a{fill-rule:evenodd;}techdegree seal-36
Michal Janek
Front End Web Development Techdegree Graduate 30,654 Points

Why do attributes classes also have super().__init__() funcs?

From what I have gathered so far from this renewed course we use super() in order to initialize init function of parent class from which the one we are working on inherits. But attributes do not inherit any so why?

class Sneaky:
    sneaky = True

    def __init__(self, sneaky=True, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.sneaky = sneaky

    def hide(self, light_level):
        return self.sneaky and light_level < 10

class Agile:
    agile = True

    def __init__(self, agile=True, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.agile = agile

    def evade(self):
        return self.agile and random.randint(0, 1)

3 Answers

Hi, Michal- I was wondering the same thing. Perhaps by me posting this, it will bring your question up in the queue again and someone more knowledgeable can answer it definitively for us. But I'll try to answer you question based on what I've learned after some research:

1) even though Sneaky and Agile both do not explicitly define a parent or super class, they both have one... it's called "object". To my knowledge, everything in Python goes back to a built-in object class. If it didn't, using "super().[etc]" inside either class would result in an error when used.

-->fyi, technically, you don't have to use "super()" if you want to call a parent's methods inside a child's class.... You can just use the parent's class name. Say a class named Word was a child of a class named Letter. Instead of writing "super().init([arguments])" in Word class, you could write "Letter.init(self, [arguments])" Note the use of "self" in the latter. A reason why "super()" is favored more than the latter example is that it makes the child class more dynamic (ex. what if you changed Letter or the inheritance hierarchy? You'd have to change the reference in the child class.) By using super(), I guess your code is not as 'tightly coupled' as Kenneth talks about.

2) In multiple inheritance (vs. single inheritance), super() is not just a link to a child's parent... it's a link to the child's entire family tree. So the MRO (method resolution order) of the Thief() class is: Thief, Sneaky, Agile, Character, [object]. From nearest in relationship to furthest away. So, if you typed in "thief1 = Thief(name='badguy', weapon='gun'), then Python passes these parameters to the Thief's init function first, then it goes to Sneaky, then to Agile, then to Character, then to object. At the end of all this, we want thief1.name = 'badguy' and thief1.weapon = 'gun'. But notice, in our example, based on the code shown in the video, .name doesn't get assigned until python gets to the Character class. And while we've made no mention of .weapon, the Character class takes care of this as well with our handy kwargs.items() and setattr code... which is great because no parameter should get to the object class (because it's init doesn't take any arguments, I think) or an error is thrown.

-->BUT, Character will never get a chance to assign .name and .weapon properly if the respective parameters do not pass through Thief, Sneaky and Agile first. That is why each of these three classes has a **kwargs argument AND why each of these classes use super()... even Sneaky and Agile. If Sneaky and Agile didn't use super() then your parameters could not continue down the chain to Character... which is why if you took out the super() reference in Sneaky or Agile, your code will throw an error.

So the kwargs dictionary we create and pack (with name='badguy' and weapon='gun'), flows in to the first class in the MRO, and that class takes any applicable parameters out of that dictionary via its init function if applicable, then effectively passes what remains of kwargs (using the "super().init" bit of code) to the next class in the MRO for that class's init function, and so on. By the time kwargs gets to the object class, it's empty, which is why an error is not thrown.

Anyway, that's my take on the video's code example using multiple inheritance and my attempt to answer your question... until someone more knowledgeable tells me differently : ) Hope this helps! D.

So awesome! This explanation was a life saver. Thank you.

Jaxon Gonzales
Jaxon Gonzales
3,562 Points

This clarified so much thanks!

Brady Huang
Brady Huang
16,360 Points

Oh my god, it's awesome explaination.

Ryan Cross
Ryan Cross
5,741 Points

The video on this subject left me pretty lost. Your explanation cleared up everything quite nicely. I think that's some helpful criticism of the video. Look at all the responses that felt the same way.

Chris Freeman
Chris Freeman
Treehouse Moderator 68,332 Points

Nice answer ds1! Thanks for the mention.

I have a few supporting comments and clarifications. Which might be redundant but in my wording.

While Agile and Sneaky do not have inherited classes (other than the default object), they are intended to be used as mixin classes along with other base classes and not on their own. Trying to use one of these mixin on its own would raise an error because, as ds1 mentioned, object.__init__ does not accept arguments.

a = Agile() # fails

The super().__init__() in each mixin will look down the MRO chain looking for the first __init__ found. Since both Agile and Sneaky utilize super(), eventually Character.__init__ will be reached if Character is the last class in the MRO.

Without the calls to super(), the init process would stop at the first mixin __init__ and not continue down the chain.

Also, notice that Thief has no __init__ method. This means it will start the init process with the first __init__ found in the inherited classes.

Not sure if I added anything different that what was said already.

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

Thanks, Chris! I really appreciate you confirming what I thought/hoped was the answer to Michal's question. And thanks for pointing out that Thief() does not have an init function... I don't want misinformation in my answer to confuse any future readers! D.

Rui Xu
Rui Xu
11,245 Points

Just check from my local environment, a = Agile() doesn't fail, but a = Agile(name='xxx') does, I believe it is a typo in your comments. :) But I get your points anyway. Thanks all for the sharing, it clears my doubts!

Quinton Dobbs
Quinton Dobbs
5,149 Points

Hopefully this post isn't too old, but if I am to understand correctly, super() passes onto whatever is next in the MRO and only passes to the object when it last in the MRO. And in this example nothing is passed to the object class because Character doesn't use super(). Is that correct?

I thought using super() allowed us to use a method from a parent - I don't understand what we mean by "passing" things on to the parent classes.

Chris Freeman
Chris Freeman
Treehouse Moderator 68,332 Points

You are correct, in the general case, that super() grants access to a method the parent class.

The "passing" occurs in the expanded case of multiple inheritance where each parent also utilize super() for the specific method overriden by the derived class. When each parent also uses super(), each subsequent parent's method is called in a chained fashion while passing the unconsumed arguments to the next parent.

The use of *args, **qwargs In the parent methods allow the passing along the unknown arguments.