Object-Oriented Programming: Polymorphism in Python

This is the 4th article in a series on Object-Oriented Programming:

Read the article about Inheritance before diving into this one.


Say we want a motorcycle class, we don’t have to worry about cargo since that’s a concern for a truck, but not for a motorcycle, we can just inherit the attributes from Vehicle, but the accelerate() method is different since motorcycles can go faster.

With motorcycles, we let them increase speed by 5 at a time.

Let’s also reimplement the vehicle_details(self) method in the Motorcycle class, changing it a bit to print a slightly different message.

Implement the code below in a ‘vehicles.py’ file and execute it.

class Vehicle:
    def __init__(self, year, model, plate_number, current_speed):
        self.year = year
        self.model = model
        self.plate_number = plate_number
        self.current_speed = current_speed

    def accelerate(self, value):
        raise NotImplementedError()

    def stop(self):
        self.current_speed = 0

    def vehicle_details(self):
        return f'{self.model}-{self.year}-{self.plate_number}'

class Truck(Vehicle):
    def __init__(self, year, model, plate_number, current_speed, current_cargo):
        super().__init__(year, model, plate_number, current_speed)
        self.current_cargo = current_cargo

    def accelerate(self, value):
        if(value < 3):
            self.current_speed += value

    def add_cargo(self, cargo):
        self.current_cargo += cargo

    def remove_cargo(self, cargo):
        self.current_cargo -= cargo

class Motorcycle(Vehicle):
    def __init__(self, year, model, plate_number, current_speed):
        super().__init__(year, model, plate_number, current_speed)

    def accelerate(self, value):
        if(value < 6):
            self.current_speed += value

    def vehicle_details(self):
        return f'Motorcycle: {self.model}-{self.year}'

if __name__ == '__main__':
    motorcycle = Motorcycle(2018, 'AI5', 'AAA0000', 0)
    truck = Truck(2015, 'V8', 'XYZ1234', 0, 0)
    vehicles = [motorcycle, truck]
    for vehicle in vehicles:
        vehicle.accelerate(2)
        vehicle.accelerate(5)
        print(vehicle.vehicle_details())
        print(vehicle.current_speed)

The output will be:

Motorcycle: AI5-2018
7
V8-2015-XYZ1234
2

In the main function, we instantiated two objects, a motorcycle and a truck, then we put them both in a list of vehicles.

We run a for loop going through the list and, for each object, we call the accelerate() method with 2 and 5, then we print the details and current speed.

As you can see, inside the loop, there is no differentiation between truck or motorcycle when we call any of the methods.

Also notice that the motorcycle object has a speed of 7 while the truck has a speed of 2, because the implementation of accelerate() for truck doesn’t allow a number higher than 2, so 5 does not apply to the truck object, but it does apply to the motorcycle that supports an acceleration of up until 5 at once. So motorcycle gets two accelerations adding to 7, while truck get only one acceleration of 2.

The vehicle_details() also produces a message ‘Motorcycle: AI5-2018’ for the motorcycle object, while it produces ‘V8-2015-XYZ1234’ for the truck.

The vehicle_details() implementation of motorcycle has ‘Motorcycle’ at the beginning and doesn’t print the plate_number.

The vehicle_details() used by the truck object is the standard that comes from the Vehicle parent class, that prints the model, the year, and the plate_number.

We have accelerate() implemented in two different classes, in two different ways, meaning many forms of implementation.

This is only possible due to Polymorphism, it comes from the greek roots "poli" (many) and "morphos" (forms).

We also did what is known as method overriding when we redefined vehicle_details() in the motorcycle class. We simply define a method in the child class with the same name of the method in the parent class and reimplement the body the way we want.

In the end, we used Inheritance to reuse code from the Vehicle class, then we used Polymorphism to call the methods with different implementations from different objects in a transparent way, and Method Overriding gave us the flexibility to adapt only the method we wanted in motorcycle while keeping the default behavior that truck inherited from Vehicle.