Dependency Injection: Python

Shivam Aggarwal
3 min readJul 18, 2018

--

Overview

Dependency Injection(DI) is a software engineering technique for defining the dependencies among objects. Basically, the process of supplying a resource that a given piece of code requires. The required resource is called dependency.
There are various classes and objects defined when writing code. Most of the time, these classes depend on other classes in order to fulfill their intended purpose. These classes, or better-used word Components, knows the resources it needs and how to get them. DI handles defining these dependent resources and provides ways to instantiate or create them externally. Dependency Container is used to implement this behavior and holds the map of dependencies for the components.
If object A depends on object B, object A must not create import object B directly. Instead of this, object A must provide a way for injecting object B. The responsibility of object creation and dependency injection are delegated to external code.

Why use Dependency Injection in your code?

  • Flexibility of configurable components — As the components are externally configured, there can be various definitions for a component(Control on application structure).
  • Testing Made Easy — Instantiating mock objects and integrating with class definitions is easier.
  • High cohesion — Code with reduced module complexity, increased module reusability.
  • Minimalistic dependencies — As the dependencies are clearly defined, easier to eliminate/reduce unnecessary dependencies.

Implementing DI in Python

Dependency Injection in Python is a little different from general static language DI. Python has a microframework library for DI, called dependency_injector. This package has two main entities: containers and providers.
Providers describe how objects are accessed. Containers are simply a collection of providers. The most commonly used provider types are: Singleton, Configuration and Factory.

Example

Following example demonstrate the usage and implementation of DI in Python. Create a file named email_client.py containing EmailClient class which depends on the config object.

Create a new file named email_reader.py which contains the EmailReader class and depends on the EmailClient object.

Now to define these above dependencies externally, create new containers.py file. Import the dependency_injector package and classes to be used in DI.

from dependency_injector import providers, containers
from email_client import EmailClient
from email_reader import EmailReader

Add class Configs to the file. This class is a container with a configuration provider which provides all the configuration objects.

class Configs(containers.DeclarativeContainer):
config = providers.Configuration('config')
# other configs

Add another class Clients. This class is a container defining all kinds of clients. EmailClient is created with a singleton provider, asserting a single instance of this class, and defining its dependency on the config object.

class Clients(containers.DeclarativeContainer):
email_client = providers.Singleton(EmailClient, Configs.config)
# other clients

The third container is Readers class, defining dependency of EmailReader class on EmailClient class.

class Readers(containers.DeclarativeContainer):
email_reader = providers.Factory(EmailReader, client=Clients.email_client)
# other readers

To run the example, create main.py file with the following code.

In the main.py file, config object is overridden with a given dictionary object. EmailReader class was instantiated without instantiating EmailClient class in main file, removing the overhead of importing or creating it. That part is taken care of by containers file.

For a working example, please refer here.

Any suggestions or thoughts, let me know:
Insta + Twitter + LinkedIn + Medium | Shivam Aggarwal

--

--