Python language basics 80: polymorphism through interfaces

Introduction

In the previous post we started looking at one of the most important ideas of object oriented design: polymorphism. Polymorphism is a way to represent common features through objects. The implementation details of the related functions through those objects do not matter to the caller. E.g. all vehicles can be accelerated. In cars you press the gas pedal, you pedal faster on a bicycle and do something else on aeroplanes. The exact acceleration process is an implementation detail of the Car, Bicycle and Aeroplane classes probably represented by a function called “accelerate” that accepts some numeric argument, such as “how_fast”.

In this post we’ll look at interface type polymorphism in Python.

Interfaces

I have to confess that the title of this post is misleading. There are no interfaces in Python, at least not in the same strict programmatic sense as you can declare interfaces in C#, Java etc. You would then implement those interfaces in various classes by some keyword such as “implements” in Java. There’s none of that Python. In fact you don’t even need classes to achieve something similar. Functions are also objects in Python and are often just enough to express polymorphism. Therefore it’s slightly different from what you’ve seen in those strictly typed languages.

We’ll look at a formatting example. When printing out the properties of an object you can have multiple formats: XML, Json, HTML, whatever.

We’ll look at a Person object that consists of several other objects: a Name, an Address, a Telephone and an age. We can print the properties of the Person object in various ways as mentioned before. However, we don’t want the Person object to worry about implementation details of the formatting itself. We don’t want to give the Person object the responsibility to know the rules and details of XML, Json, HTML, CSV etc. It’s a better design choice to let the Person object accept a function and delegate the formatting to that function. The Person object will only be responsible of passing along the properties that need to be printed by the formatter. Hopefully this process will be clearer as we go along.

OK, let’s start with the Name, Address and Telephone objects. They are all very simple with no validation to keep them short. You can put them in a Python file domains.py:

class Name:
    def __init__(self, first_name, last_name, middle_name):
        self._first_name = first_name
        self._last_name = last_name
        self._middle_name = middle_name

    def name(self):
        return self._first_name + " " + self._middle_name + " " + self._last_name


class Address:
    def __init__(self, street_name, street_number, postal_code, city):
        self._street = street_name
        self._number = street_number
        self._postal_code = postal_code
        self._city = city

    def address(self):
        return str(self._number) + " " + self._street + " " + self._postal_code + " " + self._city


class Telephone:
    def __init__(self, home, office, mobile):
        self._home_number = home
        self._office_number = office
        self._mobile_number = mobile

    def home_number(self):
        return self._home_number

    def mobile_number(self):
        return self._mobile_number

    def office_number(self):
        return self._office_number

Let’s look at the formatters. We’ll create 2 rudimentary implementations. Python can handle JSON and XML in other libraries but that’s beside the point. Consider the following functions also in domains.py:

def print_as_xml(root_name, properties_dictionary):
    print("<" + root_name + ">")
    for prop in properties_dictionary:
        print("<" + prop + ">" + properties_dictionary[prop] + "</" + prop + ">")
    print("</" + root_name + ">")

def print_as_json(root_name, properties_dictionary):
    lines = []
    print("{\"" + root_name + "\": {")
    for prop in properties_dictionary:
        lines.append("\"" + prop + "\": \"" + properties_dictionary[prop] + "\"")
    print(",".join(lines))
    print("}}")

Probably the single most important detail here is that both functions are void and accept the same type of parameters: a root name and a dictionary with all the properties we want to print. You’ll see that we print the root name followed by the property keys and values in a simple JSON and XML tree. Also, keep in mind that functions are also objects in Python and we can pass them around as input parameters.

Here’s the Person class in domain.py which demonstrates the usage of the formatter in the format_me function:

class Person:

    def __init__(self, name, address, telephone, age):
        self._name = name
        self._address = address
        self._telephone = telephone
        self._age = age

    def format_me(self, formatter):
        properties = {"name": self._name.name(), "age": str(self._age), "address": self._address.address()}
        formatter("person", properties)    

We collect the object properties in a dictionary and then delegate the actual formatting to the formatter that was passed into the format_me function. The Person knows nothing about the actual implementation of the formatter function. It’s only important the it must have two parameters otherwise the code breaks. This is how interfaces are adhered to in Python. It’s done very implicitly without the usage of specialised interfaces. If you send in a formatter function that does not accept two parameters then the interface is broken and the code throws an exception.

Let’s see how it can be used:

from domains import *

name = Name("John", "Smith", "William")
telephone = Telephone("34535", "345345", "65765754")
address = Address("New street", 34, "324 56", "Los Angeles")
person = Person(name, address, telephone, 30)
person.format_me(print_as_xml)

We construct a Person object and call its format_me function. We pass in the print_as_xml function. Again, keep in mind that we pass in a function which will then be invoked in the format_me function of the Person class. The above code produces the following result:

<person>
<address>34 New street 324 56 Los Angeles</address>
<age>30</age>
<name>John William Smith</name>
</person>

It’s simple to switch from XML to JSON:

person.format_me(print_as_json)

…which produces the following result:

{"person": {
"address": "34 New street 324 56 Los Angeles","age": "30","name": "John William Smith"
}}

Note that we didn’t have to change anything within the Person class to make this switch. Instead, we just changed the implementation details. An additional benefit is that the print_as_xml and print_as_json functions can be reused in other objects that also require formatting.

Read the next post here.

Read all Python-related posts on this blog here.

Advertisement

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: