Interfaces in Python

In the middle of building a web app template with the Tornado web framework I started thinking about how I was including a lot of code dealing with database access in my controller classes. The code is still in a prototype/proof of concept phase but I don’t like egregious cases of mixing concerns so I started thinking about how I could easily swap out providers for my persistence needs.

Now I’m sure I’m not the first person to think of this but I realized that Python makes it really easy to create classes that act like interfaces.

First we will create our class to be used as an interface.

class IPerson:
    def __init__(self, provider_instance):
        self.provider = provider_instance
        self.say_hello = self.provider.say_hello
        self.say_goodbye = self.provider.say_goodbye

We route everything through the provider instance so that the provider can implement helper functions within the class that aren’t necessarily in the interface.

Next we will create two classes that implement the interface.

class Englishman:
    def say_something(self, something):
        return str(something)
    def say_hello(self):
        return self.say_something("Good day to you, sir.")
    def say_goodbye(self):
        return self.say_something("Cheerio!")

class Cowboy:
    def say_hello(self):
        return "Howdy, partner!"
    def say_goodbye(self):
        return "Y'all come back now!"

Now we can say hello and good bye like an Englishman or a Cowboy simply by changing the argument in our interface constructor.

person = IPerson(Englishman())
person.say_hello()  # "Good day to you, sir."
person.say_goodbye()  # "Cheerio!"

person = IPerson(Cowboy())
person.say_hello()  # "Howdy, partner!"
person.say_goodbye()  # "Y'all come back now!"

With this implementation your interface actually enforces something of a contract which surprised me in the flexible world of Python. If a provider doesn’t implement something defined in the interface an AttributeError gets raised and anything not defined in the interface simply isn’t callable. This is essentially the same effect as an abstract base class but it restricts the available attributes/functions to only those defined in the interface removing the temptation to write code downstream with attributes/functions not defined in the contract.

Now I know that Google developed a dependency injection library that allows you to do essentially the same thing. This is essentially a poor man’s dependency injection that doesn’t require you to learn a complex library.

 
0
Kudos
 
0
Kudos

Now read this

Let’s build a logic bomb (for legitimate reasons) in Go

For the past couple of months I have been working on a script library tool written in Go. This is a tool that I wanted to sell and I wanted a way for people who were interested to give the tool a test drive. A lot of tools today need to... Continue →