5. Inheritance¶
Object-oriented programming is a large topic. In this book we have only scratched the surface. Some of LaunchCode’s later Skill Track courses cover OOP in detail, so you will learn a lot more later if you take one of those courses. For now, the main goal was simply to give you a taste for the topic, and to equip you with enough basic knowledge that if you see objects and classes in the wild, you will have some understanding of what is going on. Toward that end, there is one more concept that we need to mention, because it will make an appearance in Unit 2. That concept is called inheritance.
Let’s say we have a class called Cat
to represent cats. Cats can eat, sleep and make noise:
In the above example, we create a Cat
instance and store it in the variable tom
. After sleeping, tom
‘s noise()
method stops returning "prrrr"
and starts returning "meow!"
. After eating twice, he becomes tired again and switches back to "prrrr"
.
So far so good. Now let’s say we want to create another class to represent a tiger. Tigers will be pretty similar to cats, except for the following additions:
- tigers can be angry. Specifically, a tiger is angry whenever it is both hungry and tired.
- If a tiger is angry, its noise is
"GRRRR!"
.
How should we go about implementing this Tiger
class?
A Naive Tiger¶
The naive solution would be to copy and paste our Cat
class, and then modify things as necessary:
The above code works fine, but the problem with this naive solution is that we end up repeating ourselves in dozens of places. Remember that one of the core principles of good program design is that you should strive to repeat yourself as little as possible. We called this principle DRY (Don’t Repeat Yourself).
A Better Tiger¶
Ideally there should be some way of defining a Tiger
class without having to repeat all the aspects that we already defined in the Cat
class. We want to be able to say “A Tiger is exactly like a Cat, except for a few additions and modifications, which are: [blah blah blah]”.
This is exactly what inheritance allows us to do. Inheritance is a syntax for defining a custom class that inherits much of its structure and behavior from some other class. In our example, the Tiger
class inherits much of its structure and behavior from the Cat
class. Here’s how we can implement that relationship in Python:
A few key things to notice:
We define the inheritance relationship by writing
class Tiger(Cat)
. In general, the syntax for any subclass that inherits from some superclass is:class Subclass(Superclass): # method definitions for Subclass
Our
Tiger
definition is very short. This is because we only needed to define the things that distinguish aTiger
from aCat
. Specifically, we added a new method,angry
, and we modified an existing method,noise
. That’s all. The important point is that we were able to create aTiger
namedhobbes
and command him to eat and sleep, without having to write any code in ourTiger
class to define theeat
,sleep
or__init__
methods, or thetired
andhungry
attributes. We get to use all those methods and attributes “for free” just by virtue of inheriting from theCat
class.The
Tiger
class overrides thenoise
method. When we invokehobbes.noise()
, we are invoking theTiger.noise
function. This gives our tiger the opportunity to return something different than a cat would. But notice that if the tiger is not angry, then ourelse
branch contains this line:else: return Cat.noise(self)
That code essentially says: “I’m not angry, so I will just return whatever a Cat would normally return here.” In other words, the tiger defers responsibility to its cat superclass. You might say the tiger allows its more basic cat instincts to take over.
To recap: inheritance allows you to define new types like Tiger
by extending the code from previously defined types like Cat
. A subclass like Tiger
inherits all the functionality of its superclass, but can additionally define its own new attributes and methods (such as the angry
method), and can override the implementation of preexisting methods (such as the noise
method).
Another Cat Subclass¶
Let’s see another cat example: Let’s define a house cat to represent a more domesticated pet. A house cat will be just like a normal cat, except that:
- Each house cat has a name.
- A house cat is “satisfied” whenever it is not hungry or tired.
- If a house cat is satisfied, then it is able to speak English! Specifically, it can recite “Hello, my name is ___!”. Very domesticated.
This is fairly similar to the Tiger
subclass. The one new thing to notice here is that the HouseCat
class has a new attribute, .name
. Notice that we needed to override the __init__
method so that we could set the .name
attribute equal to whatever name argument was passed in.
Inheriting from Someone Else’s Class¶
One final important thing to consider is that you will often use inheritance just to customize a preexisting class that someone else created.
For example, you might decide it would be really nice if every Turtle
instance had a method called star
, which would draw a star in its current location. You can make that happen!
As you can see, all we need to do is create a new class that inherits from turtle.Turtle
, and then define the new method we wish to see.