More Fun with the Python Class Dispatcher

Published March 26th, 2010.

In a recent post, I have demonstrated how to do prototype-style method injection in Python. Today, I’ll show how you can have even more fun with the class dispatcher by changing the base class of an object during run-time. But first, let me illustrate a real-world problem where this proposed solution becomes handy…

Like many others, I’ve jumped the distributed computing hype and spent a lot of time with nosql databases (I prefer Mongo). Due to the document-based storage model, the actual document type is stored inside a given document. In example, imagine you have something like {‘type’: ‘post’, ‘id’: 23, …} stored inside a collection, say it represents a blog post. When you load an object from the database, you cannot decide what type it is unless you have retrieved it from the database. If you want to represent the retrieved data as an object, you have to add a loader that fetches the raw data and decides what type of object it should create. So, it is likely that you end up with an interface like this:

db = DB()
post = Post()
id = db.save(post)
same_post = db.get(id)
db.delete(id)

This is fairly ok, but you’ll end up splitting the interface into a db object and a post object. The db object appears reasonable because it can load the raw data and create objects of different types like Post or Comment, depending on the type variable. This is ok, but I think we can do better. Imagine an interface like this:

post = Post()
id = post.save()
same_post = Post(id)
same_post.delete()

It feels more intuitive and reflects the way you would describe the actual task. You could have the database code in the same object (or a parent of it) and make things more explicit. Though, if you cannot determine the object type before you fetch it from the database, you cannot decide what type of object to create. So, if you invoke the constructor of a post object, but you find the actual type to be “comment”, how can you change the base class now? Like this:

class Generic:
    def __init__(self, class_name=None):
        if not class_name:
            return

        classes = globals()
        if not class_name in classes:
            raise Exception("%s not found in global scope" % class_name)
        _class = classes[class_name]
        if not type(_class) == type(self.__class__):
            raise Exception("%s is not a class" % class_name)

        self.__class__ = _class

class Specialized(Generic):
    pass

c = Generic("Specialized")
print c     # prints <Specialized>

In this example,we run the constructor of class Generic and dependent on some contextual data (class_name here), we change the base class of our object after instantiation. What we get is an object of class Specialized even though we invoked the constructor of Generic. This methodology can easily be applied to our blog example, making the interface much cleaner and more expressive.