Polymorphic Python

Here at SmartFile, on the engineering side of life, we get to use a lot of awesome techniques in order to keep development fast and on-track (see the Open-Closed Principle). By using simple patterns, we are able to protect our application from design problems such as rigidity, fragility, immobility and viscosity; thus, leaving our future selves in a much, much happier state.

One technique that we use frequently is polymorphism. Before we begin to talk about polymorphic python, let’s take a look at the origin of the word.

The word polymorphism can actually be broken down into two separate greek roots. The first is poly, which is the idea of having more than one. Next, there is morph which means “of many forms.” Therefore, on an abstract level, we can assume that polymorphism means something along the lines of an object that has multiple types. In the context of software, we can think of this as the ability to manipulate similar types of data using a single common interface.

Use Cases

Polymorphism can be a difficult beast to understand at first. So, instead of just defining it, let me show you a few places it can be used. You know all sedans have four doors, an engine, and four wheels, but we have different types of sedans. You manage bank accounts, but you have different types of accounts (checking, savings, business). You have permissions, but you have different types of permissions (read, write). You want to have a single array of different types of commands or objects.

Time For Code

Scenario: We need to create a program that contains a list of people who have purchased vehicles from our shop, then print out their names.

Someone brand new to software design might reason that the code below would work.

people = ['John', 'Chris', 'Justin', 'Kyle',]
for person in people:
    print person

Output:

  • John
  • Chris
  • Justin
  • Kyle

Great! This works but isn’t very polymorphic. What if we wanted to get more details about each item in the list? Perhaps we want to know the top speed of each driver’s car, along with a generic overview of each car’s type. These items would be simple to concatenate to each string but that would make each string longer and longer as more and more data gets added. Let’s try thinking about it from an object-oriented point of view with polymorphism.

First off, we know that each of these drivers purchased a vehicle. Therefore, we can create a base class. This base class will be initiated with an owner’s name. Also, we will implement a get_owner and a top_speed method.

# Base Vehicle:
class Vehicle:
    # Constructor
    def __init__(self, owner):
         self.owner = owner
    
    def get_owner(self):
        return self.owner

    # Methods in which every subclass will be required to implement.
    def top_speed(self):
        raise NotImplementedError("Subclass is missing its top speed method.")

Note: If you’re coming from a C++ background can relate the top_speed method as a “pure virtual function”.

Now let’s go ahead and create objects for each generic type. It should look like this:

# Vehicles:
class Truck(Vehicle):
    def top_speed(self):
        return "The truck has a top speed of 120mph."
class Sedan(Vehicle):
    def top_speed(self):
        return "The sedan has a top speed of 140mph."

 

class SportsCar(Vehicle):
    def top_speed(self):
        return "The sports car has a top speed of 200mph."

Wonderful, now we can make a list which will contain these different types of objects.

vehicles = [
    Truck("Chris"),
    Sedan("Kyle"),
    Sedan("Justin"),
    SportsCar("John"),
]

This is a polymorphic list of items. Whereas they are all of a similar type, each is different in its own way. Now we just need to iterate over each object and ask for its top speed! Do not let the function overriding fool you, that is not what this article is about. The main takeaway here is to understand the idea of being able to work with different types of concrete objects at a more abstract level.

for vehicle in vehicles:
    print(vehicle.get_owner())
    print(" * " + str(vehicle.top_speed()))

Output:

Chris
* The truck has a top speed of 120mph.
Kyle
* The sedan has a top speed of 140mph.
Justin
* The sedan has a top speed of 140mph.
John
* The sports car has a top speed of 200mph.

Final Thoughts on Polymorphic Python

As you can tell, using polymorphism involved more code than the original python list, however, we have created an extendable structure that can be manipulated without a bunch of python split strings or regular expressions. We also are able to add more attributes to our data in an easier manner. Polymorphism is a very basic concept and a good foundation for other design patterns. In fact, in this article, we used a few design patterns you may not have noticed: the template method pattern and the facade pattern.

I invite you to learn more about the different types of patterns that can be implemented on top of this concept. I personally suggest the visitor, command and builder patterns as they will really start to test your knowledge of your favorite programming languages.

You can view the full code used for this article at my gist.

Technical Edits by: David Galitsky and Travis Cunningham

MASTER THE AGILE PROCESS
FREE AGILE COURSE

  • Agile Charters
  • Handling the XY Problem
  • Hiring Agile Team Members
  • Handling Impediments
  • Creating User Stories

SmartFile is a business file mangement platform that gives you more control, compliance and security.

TO SIGN UP