DI in python

code on github

Handling DI in python

Let's write a simple wiring of two services, A and B. first we can capture the behaviour of the service via two abstract base classes (ABCs), `ServiceA` and `ServiceB`, and then create multiple concrete implementations for each of these services.


from abc import ABC, abstractmethod

class ServiceA(ABC):
    @abstractmethod
    def execute(self):
        pass

class ServiceAImpl1(ServiceA):
    def execute(self):
        return "Executing Service A Impl 1"

class ServiceAImpl2(ServiceA):
    def execute(self):
        return "Executing Service A Impl 2"

class ServiceB(ABC):
    @abstractmethod
    def run(self):
        pass

class ServiceBImpl1(ServiceB):
    def run(self):
        return "Running Service B Impl 1"

class ServiceBImpl2(ServiceB):
    def run(self):
        return "Running Service B Impl 2"

Now, we can define a configuration file which will specify which implementation to be used for ServiceA and ServiceB


[SERVICES]
ServiceA = ServiceAImpl1
ServiceB = ServiceBImpl2

This file specifies ServiceA, should use ServiceAImpl1 and for ServiceB, it should use ServiceBImpl2.

Then, to use the configuration, we can use python's configparser to get the correct class implementations:


# assumes that the services and the main loading script are in the same module. 
import configparser

config = configparser.ConfigParser()
config.read('config.ini')

def get_service_implementation(service_name, default=None):
    try:
        class_name = config['SERVICES'].get(service_name, default)
        module = __import__(__name__)
        class_ = getattr(module, class_name)
        return class_()
    except (AttributeError, ImportError):
        return None # do something more appropriate... 

service_a_instance = get_service_implementation('ServiceA')
service_b_instance = get_service_implementation('ServiceB')

print(service_a_instance.execute())
print(service_b_instance.run())

this would print:


Executing Service A Impl 1
Running Service B Impl 2