Polymorphism
Note: this video contains errors in the UML diagram, these errors have been fixed below.
Now that we’ve learned how to build the structure needed for classes to inherit attributes and methods from other classes, let’s explore how we can use those classes in our code.
Polymorphism
Earlier, we defined polymorphism as “the condition of occurring in several different forms”1. In code, this allows us to create an object of a child class, and then use it just like an object from its parent class. Let’s look at an example.
from Car import *
from Airplane import *
class Main:
@staticmethod
def main():
plane = Airplane("Plane", 123, 45)
print(plane.name)
print(plane.describe())
print(plane.move(10))
car = Car("Car", 4)
print(car.name)
print(car.describe())
print(car.move(10))
# main guard
if __name__ == "__main__":
Main.main()
In this code, we are instantiating an Airplane
object and a Car
object, but we are treating them just like objects instantiated from the Vehicle
class. This is polymorphism at work! Since both Airplane
and Car
are inheriting from the Vehicle
data type, we can use any methods and access any attributes and properties that are available publicly from the Vehicle
class.
So, when we run this code, we’ll get the following output:
As we can see, even though we are calling methods defined in the Vehicle
class, it is actually using the code from the methods that were defined in the Airplane
and Car
classes, respectively. This is because those methods are overridden in the child classes.
What if we want to use the honk_horn()
method of the Car
object? Could we do that?
car = Car("Car", 4)
print(car.honk_horn())
When we run that code, we’ll see this output:
Yup! That works too. So, even though we are able to treat them as Vehicle
objects, we can still remember that this object is also a Car
object, so we can use those methods as well.
So, when it comes to polymorphism, there are a couple of important rules to remember:
- We can treat an object instantiated from any child class as an object instantiated from any parent of that class.
- We can call methods and access attributes that are declared public in the class the variable is instantiated from, as well as any parent classes.
- When we call methods that have been overridden, it will use the overridden code that is defined either in the child class, or the class nearest the child class in the inheritance hierarchy.
Another Example
Here’s another example of the power of polymorphism. In this case, we’ll create a list that stores Vehicles
, and fill that list with different types of vehicles.
from Car import *
from Airplane import *
from Truck import *
class Main:
@staticmethod
def main():
vehicles = []
vehicles.append(Airplane("Plane", 123, 45))
vehicles.append(Car("Car", 4))
vehicles.append(Truck("Truck", 157))
for v in vehicles:
print(v.name)
print(v.describe())
print(v.move(10))
# main guard
if __name__ == "__main__":
Main.main()
In this example, we are able to use a for loop to iterate over the objects in the list and call the same methods on each one. This will work as long as each object in the list has methods with the correct names, whether they appear in the object’s class definition or are inherited from a parent class.
So, when we run this program, we’ll see the following output:
Try It!
Polymorphism is a very powerful tool for object-oriented programmers. Feel free to modify the Main
class open to the left, and then run the code for this example. See if you can create Car
and Truck
objects and store them in the MotorVehicle
data type, then use the honk_horn()
method!