Python Object-Oriented Python Inheritance Super!

status 401
status 401
14,144 Points

Trying to understand super()

Hello all. Im not fully understand wht super() does. In the pratice challenge we were asked to override the add_item method. Why i had to write the add_item method again and call super() if it's already done in the parent class? I couldn't write super().add_item(item)?

What is the reason behind it?

another question i have is about "self". evreytime i change attributes in the class i have to call self?

inventory.py
class Inventory:
    def __init__(self):
        self.slots = []

    def add_item(self, item):
        self.slots.append(item)

class SortedInventory(Inventory):
    def add_item(self, item):
        super().add_item(item)
        self.slots.sort()

4 Answers

Jamison Habermann
Jamison Habermann
11,353 Points

When redefining or overriding methods in classes, super() will extend that method to the new child class. Imagine if in the Inventory class, that add_item method was actually 100 lines of code. By calling super in the SortedInventory class, you are able to call all 100 lines of that method, then extend it which is what was done here.

As far as self goes, the short answer is yes, you would have to refer to it every time. I was just as confused as you were and honestly my best advice is to keep going because it made more sense as I kept using it.

Chris Freeman
Chris Freeman
Treehouse Moderator 56,647 Points

Promoting to Answer. Thanks for contributing to the community!!

Chris Freeman
Chris Freeman
Treehouse Moderator 56,647 Points

Exactly! You want the best of both worlds: to use the same name as the parent's method and want to customize the behavior without having to rewrite all the code. The built-in function super() says "go run the parent's version, but come back here because I have more to do".

As for self, the class code explains how to construct an instance of the class. The self reference is set to the particular instance of the class that is currently being executed. This way the add_item method know in which instance of SortedInventory or Inventory to modify the slots attribute.

Post back if you need more help. Good luck!

status 401
status 401
14,144 Points

The self keyword is always used to refer to the current instance of the class? for example:

some_instance = Inventory()
some_instance.sword = "blue sword"

self refers to some_instance?

Chris Freeman
Chris Freeman
Treehouse Moderator 56,647 Points

Exactly! In your example. self would point to the same thing that some_instance points at: an object in memory

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 56,647 Points

More about self. It is literally a reference to an instance of a class. This is the same type of reference as a label assigned to an instance:

# id is effectively the object location in memory
>>> help(id)
id(obj, /)
    Return the identity of an object.

# create a basic class    
>>> class Foo:
...     def know_thy_self(self):
...         """Return id of self"""
...         return id(self)
... 
# create instance of class
>>> f = Foo()
# check id of instance
>>> id(f)
140157928256792
# check id of self
>>> f.know_thy_self
140157928256792

So self is no different than a label that also points to an instance!

bot .net asks: So self is no different than a label that also points to an instance! I just want to clear it because it's really important. in the example, f is actually being used in the method ? f is behind the scene refer to self?

You are correct that self is no different than a label f that also points to an instance! All labels point to objects in memory. In this case, self and f both point to the same instance. As a label, they are merely members of a namespace at different levels. The label f is at the top level __main__ and the label self is at the f.know_thy_self.

Using dir() you can see the namespace at any level:

Build the class

$ python
Python 3.6.3 (default, Oct  3 2017, 21:45:48) 
[GCC 7.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> dir()  # prints top level or __main__ namespace
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
# Add a class Foo.
>>> class Foo:
...     def know_thy_self(self):
...         """Print 'self' and return id of self"""
...         print(self)
...         return id(self)
... 
>>> Foo
<class '__main__.Foo'>
>>> dir()  # __main__ now has Foo
['Foo', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
>>> dir(Foo)  #  namespace of Foo. See method 'know_thy_self'.
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'know_thy_self']
>>> Foo.know_thy_self
<function Foo.know_thy_self at 0x7f589629ae18>
>>> dir(Foo.know_thy_self)  # this is the unbound method in the class. It's unbound because there is no "self"!!
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> Foo.know_thy_self.__self__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute '__self__'

Creating an instance

>>> f = Foo()  # create instance f of Foo
>>> f
<__main__.Foo object at 0x7f58961b5a90>
>>> dir()  # __main__ now has f
['Foo', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'f']
>>> dir(f)  # namespace of f has "know_thy_self"
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'know_thy_self']
>>> f.know_thy_self
<bound method Foo.know_thy_self of <__main__.Foo object at 0x7f58961b5a90>>
>>> dir(f.know_thy_self)  #  there is __self__!!! It is bound to an instance
['__call__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> f.know_thy_self.__self__
<__main__.Foo object at 0x7f58961b5a90>  # This label has the SAME value as f above

Running the method

>>> f.know_thy_self()  #prints "self" which is same value a "f"
<__main__.Foo object at 0x7f58961b5a90>
140018452224656

The id is in base 10. Converting to hexidecimal and you see that it is the same as the memory location:

>>> hex(f.know_thy_self())
<__main__.Foo object at 0x7f58961b5a90>
'0x7f58961b5a90'

As always, post back if you need more help. Good luck!!

status 401
status 401
14,144 Points

I just want to clear it because it's really important. in the example, f is actually being used in the method ? f is behind the scence refer to self?