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 - Tigerdefinition is very short. This is because we only needed to define the things that distinguish a- Tigerfrom a- Cat. 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 a- Tigernamed- hobbesand command him to eat and sleep, without having to write any code in our- Tigerclass to define the- eat,- sleepor- __init__methods, or the- tiredand- hungryattributes. We get to use all those methods and attributes “for free” just by virtue of inheriting from the- Catclass.
- The - Tigerclass overrides the- noisemethod. When we invoke- hobbes.noise(), we are invoking the- Tiger.noisefunction. This gives our tiger the opportunity to return something different than a cat would. But notice that if the tiger is not angry, then our- elsebranch 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.