5.12. OOP Inheritance Super

5.12.1. Class Variables

On static fields:

>>> class Person:
...     firstname = 'Mark'
...     lastname = 'Watney'
...     job = 'unemployed'
>>>
>>>
>>> class Astronaut(Person):
...     job = 'astronaut'
>>>
>>>
>>> astro = Astronaut()
>>>
>>> astro.firstname
'Mark'
>>> astro.lastname
'Watney'
>>> astro.job
'astronaut'

5.12.2. Dynamic Attributes

>>> class Person:
...     def __init__(self):
...         self.firstname = 'Mark'
...         self.lastname = 'Watney'
...         self.job = 'unemployed'
>>>
>>>
>>> class Astronaut(Person):
...     def __init__(self):
...         self.job = 'astronaut'
>>>
>>>
>>> astro = Astronaut()
>>> print(vars(astro))
{'job': 'astronaut'}

5.12.3. Super

  • Order is important

  • Raymond Hettinger - Super considered super! - PyCon 2015 1

>>> class Person:
...     def __init__(self):
...         self.firstname = 'Mark'
...         self.lastname = 'Watney'
...         self.job = 'unemployed'
>>>
>>>
>>> class Astronaut(Person):
...     def __init__(self):
...         super().__init__()
...         self.job = 'astronaut'
>>>
>>>
>>> astro = Astronaut()
>>> print(vars(astro))
{'firstname': 'Mark', 'lastname': 'Watney', 'job': 'astronaut'}
>>> class Person:
...     def __init__(self):
...         self.firstname = 'Mark'
...         self.lastname = 'Watney'
...         self.job = 'unemployed'
>>>
>>>
>>> class Astronaut(Person):
...     def __init__(self):
...         self.job = 'astronaut'
...         super().__init__()
>>>
>>>
>>> astro = Astronaut()
>>> print(vars(astro))
{'job': 'unemployed', 'firstname': 'Mark', 'lastname': 'Watney'}

5.12.4. Super Init

>>> class Person:
...     def __init__(self):
...         print('Person init')
>>>
>>>
>>> class Astronaut(Person):
...     pass
>>>
>>>
>>> astro = Astronaut()
Person init
>>> class Person:
...     def __init__(self):
...         print('Person init')
>>>
>>>
>>> class Astronaut(Person):
...     def __init__(self):
...         print('Astronaut init')
>>>
>>>
>>> astro = Astronaut()
Astronaut init
>>> class Person:
...     def __init__(self):
...         print('Person init')
>>>
>>>
>>> class Astronaut(Person):
...     def __init__(self):
...         super().__init__()
...         print('Astronaut init')
>>>
>>>
>>> astro = Astronaut()
Person init
Astronaut init
>>> class Person:
...     def __init__(self):
...         print('Person init')
>>>
>>>
>>> class Astronaut(Person):
...     def __init__(self):
...         print('Astronaut init')
...         super().__init__()
>>>
>>>
>>> a = Astronaut()
Astronaut init
Person init

5.12.5. Init and Multiple Inheritance

Multiple inheritance in Python needs to be cooperative. That is, the two parent classes need to be aware of the possibility that each other exist (though they don't need to know any of each other's details). Then whichever parent is named first can call the other parent's __init__ method. That's how super works, it always calls the next class in the MRO (the method resolution order) of the instance being operated on.

>>> class HasPosition:
...     def __init__(self):
...         self.x = 0
...         self.y = 0
>>>
>>> class HasHealth:
...     def __init__(self):
...         self.health = 100
>>>
>>>
>>> class Hero(HasPosition, HasHealth):
...     def __init__(self, name):
...         self.name = name
...         super().__init__()
>>>
>>>
>>> h = Hero('Mark Watney')
>>> vars(h)
{'name': 'Mark Watney', 'x': 0, 'y': 0}
>>> class HasPosition:
...     def __init__(self):
...         self.x = 0
...         self.y = 0
>>>
>>> class HasHealth:
...     def __init__(self):
...         self.health = 100
>>>
>>>
>>> class Hero(HasPosition, HasHealth):
...     def __init__(self, name):
...         self.name = name
...         super().__init__()
...         super().super().__init__()
>>>
>>>
>>> mark = Hero('Mark Watney')
Traceback (most recent call last):
AttributeError: 'super' object has no attribute 'super'
>>> class HasPosition:
...     def __init__(self):
...         self.x = 0
...         self.y = 0
>>>
>>> class HasHealth:
...     def __init__(self):
...         self.health = 100
>>>
>>>
>>> class Hero(HasPosition, HasHealth):
...     def __init__(self, name):
...         self.name = name
...         x = super()
...         print('Obj:', x)
...         print('Type:', type(x))
...         print('Vars:', vars(x))
...         print('Dir:', dir(x))
>>>
>>> mark = Hero('Mark Watney')
Obj: <super: <class 'Hero'>, <Hero object>>
Type: <class 'super'>
Vars: {'name': 'Mark Watney'}
Dir: ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__self_class__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__thisclass__', 'name']

5.12.6. Init Subclass

>>> class HasPosition:
...     def __init_subclass__(cls, **kwargs):
...         super().__init_subclass__(**kwargs)
...         cls.position_x = 0
...         cls.position_y = 0
>>>
>>>
>>> class HasHealth:
...     def __init_subclass__(cls, **kwargs):
...         super().__init_subclass__(**kwargs)
...         cls.health = 100
>>>
>>>
>>> class Hero(HasPosition, HasHealth):
...     def __init__(self, name):
...         self.name = name
>>>
>>>
>>> vars(Hero)  
mappingproxy({'__module__': '__main__',
              '__init__': <function Hero.__init__ at 0x...>,
              '__doc__': None,
              'health': 100,
              'position_x': 0,
              'position_y': 0})
>>>
>>>
>>> hero = Hero('Mark')
>>> vars(hero)
{'name': 'Mark'}

Init subclass can also take keyword arguments:

>>> class Person:
...     def __init_subclass__(cls, /, job, **kwargs):
...         super().__init_subclass__(**kwargs)
...         cls.job = job
>>>
>>>
>>> class Astronaut(Person, job='astronaut'):
...     pass

5.12.7. Use Case - 0x01

>>> x = True
>>>
>>>
>>> type(x)
<class 'bool'>
>>>
>>> bool.mro()
[<class 'bool'>, <class 'int'>, <class 'object'>]
>>>
>>>
>>> isinstance(True, bool)
True
>>>
>>> isinstance(True, int)
True
>>>
>>> isinstance(True, object)
True

5.12.8. References

1

Hettinger R. Super considered super!. PyCon 2015. Year: 2020. Retrieved: 2022-07-13. URL: https://www.youtube.com/watch?v=EiOglTERPEo