Fun with the Python Class Dispatcher

Published May 16th, 2008, updated March 26th, 2010.

In object oriented programming, the class dispatcher is a built-in function which is invoked whenever you access an object’s method. When this happens, the class dispatcher looks up a given method in the base class of that object and it’s ancestors and executes it in the context of the given object. From there, it’s common practice to write code like this:

# class-based programming style
class Foo:
    pass

class Bar(Foo):
    def bar(self):
        print "bar"

bar = Bar()

bar.bar() # prints "bar"

In this example, we create a base class Foo. We subclass Foo as Bar and add the method bar() to adapt the class Bar to our needs. This methodology is straight forward if you have complete control over the source code. However, if you try to do minor changes somewhere upwards in the inheritance tree, you would have to copy lots of code and take care of all inheritors. Obviously, this is a bad programming style and impractical in many cases.

Though, Python’s class dispatcher uses dynamic delegation and you can do something called method injection. This is a programming style that is typical for prototype-based programming/classless programming which is default in JavaScript in example. Here, you avoid sub-classing in favor of method injection. See below:

# prototype-based programming style
class Foo:
    pass

foo = Foo()

def bar(self):
    print "bar"

foo.__class__.bar = bar

foo.bar() # prints "bar"

In this example, we inject the method bar into the base class of object foo (which is Foo). This enables us to modify Foo and all of its inheritors after sub-classing and instantiation took place.

This methodology becomes pretty handy if you write plug-ins for some software that does not export proper interfaces for plugins or if you cannot change the code of some inheritors. Basically, this trick works because the class dispatcher uses dynamic delegation: Objects and classes are inspected at call-time and so, the dispatcher finds attributes even if they are added after object instantiation.

  • Hi there,

    Not so clever. I don't think this is really what you want. Consider this example.

    >>> foo1 = Foo()

    >>> foo2 = Foo()

    >>> bar = lambda self: "bar"

    >>> foo1.__class__.bar = bar

    >>> foo2.bar
    <bound foo.<lambda="" method=""> of <__main__.Foo instance at 0xb78a3b6c>>

    >>> foo2.bar()
    'bar'

    Not quite 'prototype'-based programming, as by adding 'bar' to foo1, you've also affected all instances of Foo.

    You probably need something like this:
    http://loveandtheft.org/2008/09/11/prototype-based-programming-in-python/</bound>
  • 2600 hertz
    very clever...
  • "nor you want to change Foo."

    Surely foo.__class__ *is* Foo, so you are modifying Foo?
  • Yes - I meant you can modify Foo during run time, without modifying the source file and without sub-classing Foo. If you write a plugin for some software with inappropriate hooks, you can use this method to change the object's behavior in-place. This leaves the original source code untouched and you can update it easily without the need of patching every version.

    This is a clear advantage of the prototype-based programming style, which is the reason why I mentioned it here [http://loveandtheft.org/2008/09/11/prototype-based-programming-in-python/].
blog comments powered by Disqus