29. Inheritance in depth
Chapter 27 touched on inheritance: one class
building on another. It is how you avoid writing the same code twice. A
Dog and a Cat are both Animals —
same name, same way of being described, only differing in the
sound they make. Inheritance writes the shared part once.
The base class
Start with a general class. Everything common to the family lives here:
class Animal:
def __init__(self, name):
self.name = name
def describe(self):
print(f"{self.name} is an animal.")A child class that inherits
Name the parent in parentheses, then call
super().__init__() to run the parent's constructor:
class Dog(Animal):
def __init__(self, name):
super().__init__(name) # run Animal's __init__Dog has no describe of its own, but it
inherits from Animal, so a Dog instance finds
it there:
rex = Dog("Rex")
rex.describe() # Rex is an animal. (inherited from Animal)Python looks up describe on rex, does not
find it, then checks Dog, does not find it there either,
and finally finds it on Animal.
Adding behaviour the child alone has
Give the child methods the parent lacks. They are defined on the child class, invisible to the parent and to sibling classes:
class Dog(Animal):
def __init__(self, name):
super().__init__(name)
def fetch(self):
print(f"{self.name} fetches the ball.")
rex = Dog("Rex")
rex.fetch() # Rex fetches the ball.Overriding: replacing a parent method
If the child defines a method with the same name as the parent's, the child's version is found first and wins — this is overriding:
class Dog(Animal):
def __init__(self, name):
super().__init__(name)
def describe(self):
print(f"{self.name} is a dog.")
rex = Dog("Rex")
rex.describe() # Rex is a dog. (Dog's version, not Animal's)Now Python finds describe on Dog and never
looks at Animal.
Calling the parent's version too
Sometimes you want to extend the parent's method, not
replace it: do what it does, then add to it. Use super() to
call the parent's version:
class Dog(Animal):
def __init__(self, name):
super().__init__(name)
def describe(self):
super().describe() # do the Animal part first
print(" ...specifically, a dog.") # then the Dog part
rex = Dog("Rex")
rex.describe()
# Rex is an animal.
# ...specifically, a dog.super().describe() runs Animal.describe on
this instance, then the Dog-specific line follows. This "call up to the
parent" is one of the handiest tools in object-oriented code.
Open exercises/29/01-animals.py. It has
Animal and Dog. Add a Cat that
inherits from Animal, overrides describe to
say it is a cat, and adds a meow method. Make one cat and
call both.
Checking types with isinstance
isinstance(obj, ClassName) returns True if
obj is an instance of that class or any subclass:
rex = Dog("Rex")
print(isinstance(rex, Dog)) # True
print(isinstance(rex, Animal)) # True (Dog is a subclass of Animal)This lets you write code that acts differently depending on what kind of object it has received.
Homework
Homework files are in exercises/29/homework/.
Problem 1 — Vehicle and Car
Open exercises/29/homework/01-vehicle.py. Build a base
Vehicle class with __init__(name) and a
describe() that prints
<name> is a vehicle.. Then build a Car
that inherits from it. Make a car and call describe() — it
should use the inherited method.
Problem 2 — Override
Open exercises/29/homework/02-override.py. Starting from
the Vehicle/Car classes, override
describe() on Car to print
<name> is a car. instead. Confirm a
Vehicle still says "vehicle" and a Car says
"car".
Problem 3 — Extend the parent
Open exercises/29/homework/03-extend.py. Override
Car.describe() so it first calls
super().describe(), then prints an extra line,
It has four wheels.. Both lines should appear.
Challenge — Two children
Open exercises/29/homework/04-two-children.py. Build
Animal plus two children, Bird and
Fish. Each overrides move() to print how it
gets around (flies, swims). Make one of each,
put them in a list, and loop over it calling move().
Stuck or finished? Open the homework solutions page.