August 5, 2008

Dennis Kempin's Blog

Dennis Kempin

Dependency Injection in Python

At the moment I am writing a simple IRC service bot for our Open4Free IRC network, during development I remembered a pattern called Dependency Injection, also known as Inversion of Control (IoC). While this Pattern is spread in Java World, the Python community has very few references to this topic.

The reason why I want to use Inversion of Control in my Bot is to provide an extensible architecture that shall be able to access and define shared services. Well the Python version of this pattern is extremely simple, since Python is highly flexible.

import inspect
 
class Inject(object):
    def __init__(self, interface):
        self.interface = interface
 
class Container(object):
    def __init__(self):
        self.implementations = {}
        self.instances = {}
 
    def addComponent(self, interface, implementation=None):
        self.implementations[interface] = implementation or interface
 
    def getComponent(self, interface):
        if interface in self.instances:
            return self.instances[interface]
        if interface not in self.implementations:
            return None
 
        cls = self.implementations[interface]
        instance = self.instances[interface] = cls.__new__(cls)
 
        for key, classvalue in inspect.getmembers(cls):
            if isinstance(classvalue, Inject):
                value = self.getComponent(classvalue.interface)
                setattr(instance, key, value)
 
        instance.__init__()
        return instance

With this code you can register any new-style class and inject other instances into it by adding an instance of Inject into the class values.

class A(object):
    def test(self):
        print "A.test()"
 
class B(object):
    a = Inject(A)
 
    def test(self):
        self.a.test()
        print "B.test()", self
 
test = Container()
test.addComponent(A)
test.addComponent(B)
 
test.getComponent(B).test()

And of course you use interfaces to make components replaceable, or just replace the implementation of some components by other implementations.

class A(object):
    def test(self):
        print "A.test()"
 
class B(object):
    a = Inject(A)
 
    def test(self):
        self.a.test()
        print "B.test()", self
 
class ImprovedA(A):
    def test(self):
        print "ImprovedA.test()"
 
test = Container()
test.addComponent(A, ImprovedA)
test.addComponent(B)
 
test.getComponent(B).test()

by Dennis at10:53 PM under dependency injection, inversion of control, python (Comments)