Python Object-Oriented Python Inheritance Multiple Superclasses

confused with the super, init and tightly loosed code

I am having a hard time grasping how the super function works, i believe it is meant for a subclass to inherit the the functions of a parent class. In Kenneth's example though it can be also be used to inherit just a method? So does this mean i can inherit/ transfer just a method from the other modules as long as i have imported them?

And so everytime is use the super() does this mean i have to include the __init__ when setting attributes of my current module, why do we write "def __init__ (character,sneaky,agile) and the write the second super().__init__(args,kwargs), i still don't get what the second __init__ is for??

Another thing is instead of writing name as part of the arguments, doesn't adding args solve that instead?? Why do we move the character in the thieves module to be the last argument?? What difference does doing that make.

[MOD: added formatting -cf]

2 Answers

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 59,736 Points

Here is my view on Inheritance and using Super (and namespaces).

When a class is defined is namespace dictionary is created hold all of the locally defined attributes (this includes methods). When an class attribute is accessed, it is looked up in the local namespace dictionary.

But what if the name is not found in the local namespace? The inheritance order (Method Resolution Order - MRO) is searched for the reference by looking at each parent's namespace until the first match is found. This is why an __init__ is not needed in the Thief class. It is searched for and found in the first parent class - the Agile class. With this local, then parental, namespace searching, all parent methods are automatically included in the local child class.

Great, but what if you want a different behavior? We can "block" the parent version by creating a local method version that will be "seen" before the parent version. This is referred to as "overriding" the parent method.

OK, but what if you only want to modify the parent's method behavior without completely overriding it? This is where super() is used. The super().method_name function says, "search the MRO for the first method_name match, execute that method, then return and continue executing locally." The use of super can repeat recursively if the called parent method also includes a super() call.

The flow when Thief() is call go something like:

  • call Thief.__init__(), not found
  • check MRO, found `Agile.init(), begin execution
  • In Agile.__init__(), hit super().__init__() call, check MRO, found Sneaky.__init__(), begin execution
  • In Sneaky.__init__(), hit super().__init()__() call, check MRO, found Character.__init__(), begin execution
  • in Character.__init__(), complete execution, return to Sneaky.__init__()
  • in Sneaky__init__(), complete execution, return to Agile.__init__()
  • in Agile.__init__(), complete execution, return`

If Character was the first parent class, its __init__ method would return to Thief and not continue to Agile.__init__() or Sneaky.__init__()because there isn't a super() call in Character.__init__.

The "second __init__" in super().__init__ used to tell super() which method to execute.

Regarding args and kwargs, they contain only the left over arguments that aren't assigned to a local parameter.

  • Thief has no __init__ all arguments are interpreted by Agile.__init__().
  • Agile.__init__() defines the agile parameter, so the first positional argument in args or the keyword "agile" argument would be assigned to this parameter. The remaining positional arguments are assigned to a new args and the remaining keyword arguments are assigned to a new kwargs. This is why "Kenneth" needs a keyword argument: so the string "Kenneth" is not assigned to the agile parameter.
  • Sneaky.__init__() defines the sneaky parameter, so the first positional argument in args or the keyword "sneaky" argument would be assigned to this parameter. The remaining positional arguments are assigned to a new args and the remaining keyword arguments are assigned to a new kwargs.
  • Character.__init__() defines the name parameter, so the first positional argument in args or the keyword "name" argument would be assigned to this parameter. The remaining positional arguments are assigned to a new args and the remaining keyword arguments are assigned to a new kwargs.

If you were to use Thief("Kenneth", agile=True) you would get an error that "keyword agile has already been assigned" due to "Kenneth" being assigned to agile parameter before the keyword argument is evaluated.

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

Such a complicated answer for a beginner course.

Look at the amount of waffle that is there its nearly a 2 page document just to explain a method looks like it has been copied and pasted from wiki.

adrian miranda
adrian miranda
13,529 Points

The use of super() is to actually call a method in a class that you are inheriting from.

So, if you create a __init__ method in your class, it will overwrite the __init__ method from the parent class. The thing is, sometimes you actually still want you to do the same things that the parent class does. But by default they won't happen.

Maybe you could write those things out, but that is wasteful and error prone. It makes more sense to just call super().__init__ and now all of the stuff n your parent class __init__ method will happen. If someone enhances the parent class and it's __init__ in the future, you will automatically get the newest code.

[Mod: added formatting. -cf]

Thank you!that helps a lot