Python language basics 83: polymorphism through inheritance III
February 28, 2016 Leave a comment
Introduction
In the previous post we started working on a scenario where class inheritance can be advantageous. We built two classes, Dog and Duck, and we saw that they had some similarities and also some unique features.
In this post we’ll see how to apply inheritance in code.
Abstract classes and methods
Recall our analysis of the Dog and Duck classes:
- Both Dog and Duck have identical initialisers: they require a name and an age
- Both of them have a make_sound and number_of_legs functions but they are implemented differently
- Both have a describe_me method with identical implementations
- Both have unique functions not found in the other: the Dog has catch_intruders and the Duck has what_is_so_special and average_weight
We can factor out the similarities to a so-called abstract class, a.k.a. a base class or superclass. We can call that class Animal.
Let’s build it step by step. We know that it needs an initialiser with name and age as parameters:
class Animal: def __init__(self, name, age): self._name = name self._age = age
Both classes have a make_sound and number_of_legs functions but we cannot factor them out so easily since they are different method bodies.
The describe_me methods are however identical, so let’s extend the Animal class:
class Animal: def __init__(self, name, age): self._name = name self._age = age def describe_me(self): print("My name is {}, I am {} years old , I have {} legs. My language sounds as follows: {}".format(self._name, self._age, self.number_of_legs(), self.make_sound()))
Note that the Animal class has no make_sound and number_of_legs functions on its own. They are so-called abstract functions. In Java or C# we would have to declare them with the abstract keyword and no method implementation. In Python it’s enough to append them to “self”. Since Animal doesn’t implement them we can say that Animal is an abstract class. It’s abstract and that implies that we cannot instantiate an Animal class. Well, we can to be exact, but we cannot really use it:
a = Animal("Missy", 5) a.describe_me()
This will result in a runtime error:
AttributeError: ‘Animal’ object has no attribute ‘number_of_legs’
Those functions are implemented in the derived classes, also called subclasses or concrete classes and even implementing classes, in our case Dog and Duck. How do we connect Dog and Duck to Animal then? We do it with parenthesis as follows:
class Dog(Animal): def make_sound(self): return "Woooff" def number_of_legs(self): return 4 def catch_intruders(self, number_of_intruders): print("Caught {} intruders!".format(number_of_intruders)) class Duck(Animal): def make_sound(self): return "Quack" def number_of_legs(self): return 2 def what_is_so_special(self): return "I am known for duck typing" def average_weight(self): return 3
Note how we got rid of the common constructor and the describe_me method. They are now contained within the Animal class which Dog and Duck inherit from.
Our previous test code…
d = Dog("Elvis", 3) d.describe_me() d.catch_intruders(3) duck = Duck("Ducky", 4) duck.describe_me() print(duck.what_is_so_special()) print(str(duck.average_weight()))
…will work exactly as before.
That’s the basics of class inheritance in Python. We’ll start discussing file related operations in the next post.
Read all Python-related posts on this blog here.