Interface Driven Programming In Python

If it looks like a duck, quacks like a duck and walks like a duck, it’s a duck

Probably the greatest feature of python is its dynamic nature.  By this we mean that variable names are not bound to types and they also can be assigned at run time, this concept is also know as duck typing and an example of it is the following:

#!/usr/bin/python
class Person(object):
        pass


def print_name(obj):
    ''' Expects obj to expose a property name'''
    print obj.name

p = Person()
# we add name dynamically
p.name = 'John'

# print name
print_name(p)

# we can change name from a string to anyother type
p.name = 123

# print name continues to work
print_name(p)

At this point, you should notice that the print_name function, silently is making the assumption that obj is supporting the attribute name. Of course this will not always be the case since we can pass any kind of a variable to the function causing an exception to be thrown.

It’s easier to ask forgiveness than permission

A very common solution for this kind of problem in python is the following:

def print_name(obj):
    try:
        print obj.name
    except TypeError:
        pass

This pattern is commonly known as It’s easier to ask forgiveness than permission. In other words we assume that the passed in object indeed supports name and we try to use it, if it turns out that it not supported an TypeError exception will be raised and caught allowing our program to continue to function.

Look before you leap

Another way to achieve similar behaviour, is to apply a technique known as Look before you leap meaning that, we verify that an object is supporting a specific operation before we try to use it. The following code is an example of this technique:

def print_name(obj):
    if isinstance(obj, Person):
        print obj.name

Another way to achieve a similar effect is the following:

def print_name(obj):
    if hasattr(obj, "name"):
        print obj.name

Possible Problematic Behaviour

Although the solutions we have seen so far, can allow a program to continue its execution without breaking in case of a wrong argument, we still can see some problematic behaviour that might cause side effects, possibly leading to bogus behaviour.

For example, checking for the type of the passed object will ignore any other object than Person defining name which might not be our intention. Also using the hasattr again might lead to cases like the following:

class Person(object):
    name = 'To be defined'

def print_name(obj):
    if hasattr(obj, "name"):
        print obj.name

print_name(Person)

The output of this snippet will be:

To be defined

while what would have seen more reasonable in this case would have been something like this:


def print_name(obj):
    if hasattr(obj, "__name__"):
        print obj. __name__

# do your stuff ...

With the following output:

Person

Another example of undesired behaviour can be demonstrated in the following example:

class Person:
    def __init__(self, name):
        self._name = name

    def get_name(self):
        return self._name;

def print_name(obj):
    if isinstance(obj, Person):
        print obj.get_name()


person = Person('john')

# print_name will work as expected
print_name(person) 

# we delete name from person
del person._name    

# now we will have an unhandled exception!
print_name(person) 

Documentation Issues

More than the problems I discussed so far as far as duck typing is going, there are also issues with proper documentation. Given the flexibility of dynamic typing, chances are that users of specific module will have to delve in directly to the source code trying to understand its details and how it is working. A developer who just started using a system, is very possible to misuse it and even start use attributes consisting implementation details breaking encapsulation, making the maintenance of the code difficult and error prone.

The problem became obvious since python’s early stages

As early as 2001, we can find proposals like: PEP 245 and PEP 246 trying to address the problem we are discussing here.

The discussion was very long and controversial since it was about a change that seemed not pythonic at all!

A few years latter Guido van van Rossum came with his notorious blog posting:Interfaces or Abstract Base Classes? which led to PEP 3119 introducing the concept of Abstract Base Classes as we use it today.

In this posting we will not deal with historical aspects of ABCs (Abstract Base Classes) nor with their non pythonic nature but we will focus in understanding their implementation mechanics and details, so keep on reading to the next page!

Leave a Reply