3.7 Inheritance: Tracing Initialization#
Let’s trace what happens when we initialize an object in the context of inheritance. We’ll use a fresh example about monsters:
from typing import Any
class Monster:
def __init__(self, name: str) -> None:
self.name = name
self.hunger = 100
self.energy = 0
def eat(self, item: Any) -> None:
raise NotImplementedError
def sleep(self, minutes: int) -> None:
self.energy += minutes
def __str__(self) -> str:
return self.name + 'Boo!'
class Gremlin(Monster):
def __init__(self, name: str) -> None:
Monster.__init__(self, name)
self.stuffy = 'Bunny'
def eat(self, item: Any) -> None:
# Eating makes Gremlins more hungry!
self.hunger = self.hunger + 10
def __str__(self) -> str:
return self.name + 'Mwahaha!'
if __name__ == '__main__':
m = Gremlin('Julius')
In the main block, when we call Gremlin('Julius')
we invoke the three-step
process for creating an instance of a class:
Create a new
Gremlin
object behind the scenes.Call
__init__
with the new object passed to the parameterself
, along with the other argument,'Julius'
.Return the new object (which we then assign to variable `m``, which must first be created).
Let’s go through these steps.
In step 1, a new Gremlin
object is created for us, and is currently empty.
This is the state of memory afterwards:
In step 2, the __init__
method for Gremlin
is called,
with the new object passed as the parameter self
,
and the other argument, 'Julius'
, passed as name
.
The Gremlin
object is still empty.
At the moment after the Gremlin
initializer has been called, this is the
state of memory:
If we look at the code for the Gremlin
initializer, we see that
the first thing this method does is call the initializer for Monster
,
passing the values of self
and name
.
The Gremlin
object is still empty.
The Monster
initializer has three lines of code.
It use self
to access the new Gremlin
object
and defines three new attributes within it, giving each a value:
When the Monster
initializer returns, we resume the Gremlin
initializer
where we left off.
It has one line of code left, which uses self
to access the Gremlin
object
and put a new attribute called stuffy
inside it:
Finally, step 2 is done and we pop the Gremlin
initializer call from the stack.
We are back in the __main__
block where we were in the middle of completing
an assignment statement: m = Gremlin('Julius')
.
We have evaluated the right-hand side, yielding the id of a Gremlin
object, id106.
We will assign it to m
, but since there is no m
in the frame where we are
working, we make one first:
A lot had to happen to execute that one-line main block!