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! While you're at it, check out some resources Treehouse students have shared here.

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

JC Canapi
JC Canapi
2,097 Points

Python OOP classes Sneaky and Agile super() clarification

Hi,

I would like to get clarification with regards to the use of super() in the attributes.py. Why do we need super() below each init we are not referring to another super class? Or am I missing something?

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)

(MOD: Just updated the code formatting so it's easier to read. See the Markdown Cheatsheet below the text box when writing, for how to do the formatting. Don't worry, it's easy!)

1 Answer

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 68,423 Points

Great question! The super() function refers to calling the next parent class in the Method Resolution Order (MRO). But wait, you say, Sneaky and Agile do not have an parent classes. Even if there are no classes listed, all classes are assumed to inherit from the base class object.

>>> class foo:
...     def __init__(self, *args, **kwargs):
...         super().__init__(*args, **kwargs)
... 
>>> f = foo()
>>> foo.__class__.__mro__
(<class 'type'>, <class 'object'>)

In this case, the super() call in foo.__Init__() would call object.__init__() which is basically a do nothing function at the base level that does not accept arguments. This means that any arguments not consumed by the Sneaky or Agile __init__ method would get passed to object.__init__() and an error would be raised:

>>> 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
... 
>>> s = Sneaky(True)
>>> s
<__main__.Sneaky object at 0x7f547234d198>
>>> s.sneaky
True
>>> s = Sneaky(True, "bob")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in __init__
TypeError: object.__init__() takes no parameters

Due to the presence of super().__init__(*args, **kwargs) in both Agile and Sneaky, and since any arguments passed on to object.__init__() will raise a TypeError, then the classes Agile and Sneaky can only safely be used as part of a multiple inheritance chain where the last class on the chain either,

  1. handles all *args and **kwargs passed in, or
  2. does not call super.

So the bottom line is super() is needed in Agile and Sneaky precisely because both are intended to be used solely as "MixIn" (see What is a MixIn) classes to amend the behavior of another class on the inheritance list. In this case, they are both used to add attributes and methods to the base Character class.

It is standard convention of an "MixIn" class to include a super().__init__(*args, **kwargs) statement in the __init__ method so that all extra arguments are passed down the MRO chain to be used or passed further along, as needed, by subsequent classes.

Post back if you have more questions. Good luck!!

JC Canapi
JC Canapi
2,097 Points

Thanks Chris, this clarifies the use of super() for me. :)