What is Dependency Injection In Python

Share Your Love

Dependency Injection in Python which is also known as DI, is a design pattern used in software engineering to manage the dependencies between different components of an application. The basic idea behind DI is to decouple the creation of an object from its dependencies and provide them externally. In other words, instead of creating objects inside other objects, the dependencies are injected into the objects from outside.

The main benefits of using DI are:

  1. Testability: By injecting dependencies into an object, it becomes easier to write automated tests for the object. This is because the dependencies can be replaced with mock objects or test doubles that mimic the behaviour of the real dependencies.
  2. Flexibility: By injecting dependencies into an object, it becomes easier to change the behaviour of the object at runtime without modifying its code. This is because the dependencies can be swapped out with different implementations of the same interface.

There are three types of dependency injection:

  1. Constructor Injection: Dependencies are injected via a class constructor.
  2. Setter Injection: Dependencies are injected via a set method or property.
  3. Interface Injection: Dependencies are injected via an interface that the object implements.

Examples of 3 Dependency Injection:

Here’s an example of constructor injection in Python:

Suppose you have a class Car that depends on a class Engine to function. Instead of creating an instance of Engine inside the Car class, you can inject the dependency via the constructor:

class Engine:
    def start(self):
        print("Engine started.")

class Car:
    def __init__(self, engine):
        self.engine = engine

    def start(self):
        self.engine.start()
        print("Car started.")

engine = Engine()
car = Car(engine)
car.start() # Output: "Engine started. Car started."

In the above example, the Car class depends on the Engine class to function. The Engine class is injected into the Car class via the constructor. When the start() method of the Car class is called, it calls the start() method of the injected Engine object first and then prints “Car started.” This way, the Car class is decoupled from the implementation details of the Engine class and can be easily tested and modified.

Example of setter injection in Python:

class User:
    def __init__(self):
        self.db_connection = None

    def set_db_connection(self, db_connection):
        self.db_connection = db_connection

    def get_user(self, user_id):
        # use the db_connection to retrieve user information

In this example, the User class has a setter method called set_db_connection that takes a database connection object as its argument. The setter method sets the db_connection instance variable of the User class to the provided connection object. The get_user method of the User class uses the db_connection instance variable to retrieve user information from the database.

To use the User class with setter injection, you first create an instance of the class and then inject the database connection object using the set_db_connection method, like this:

from some_db_module import create_db_connection

user = User()
db_connection = create_db_connection()
user.set_db_connection(db_connection)
user.get_user(123)

In this example, the create_db_connection function returns a database connection object that is then injected into the User instance using the set_db_connection method. This way, the User class is decoupled from the implementation details of the database connection and can be easily tested and modified.

Example of Interface Injection in python:

Interface injection is a type of dependency injection where an object depends on an interface rather than a concrete implementation. This allows the object to be more flexible and configurable at runtime, as different implementations of the interface can be injected.

Here is an example of interface injection in Python:

class IDataAccess:
    def get_data(self, query):
        pass

class SqlDataAccess(IDataAccess):
    def get_data(self, query):
        # implementation for SQL data access
        pass

class XmlDataAccess(IDataAccess):
    def get_data(self, query):
        # implementation for XML data access
        pass

class DataAccessService:
    def __init__(self, data_access):
        self.data_access = data_access

    def get_data(self, query):
        return self.data_access.get_data(query)

In this example, there is an interface called IDataAccess that defines a method called get_data(). Two classes, SqlDataAccess and XmlDataAccess, implement this interface with different implementations of the get_data() method for accessing data from SQL and XML data sources, respectively.

The DataAccessService class depends on the implementation of the IDataAccess interface, which is injected via the constructor. This allows the DataAccessService class to be more flexible and configurable, as different implementations of the IDataAccess interface can be injected at runtime.

Here’s an example of how you could use this code:

sql_data_access = SqlDataAccess()
data_access_service = DataAccessService(sql_data_access)
result = data_access_service.get_data("SELECT * FROM customers")

xml_data_access = XmlDataAccess()
data_access_service = DataAccessService(xml_data_access)
result = data_access_service.get_data("SELECT * FROM customers")

In this example, two instances of the DataAccessService class are created with different implementations of the IDataAccess interface injected. The first instance uses the SqlDataAccess implementation to access SQL data, while the second instance uses the XmlDataAccess implementation to access XML data.

Real Time Example Dependency Injection:

So here is a real-time example of Dependency Injection in action:

Suppose you are building an e-commerce platform that allows users to buy and sell products online. The platform has a Checkout class that handles the checkout process. The Checkout class depends on a payment gateway to process the payment.

Without Dependency Injection, the Checkout class would create an instance of the payment gateway directly. Here’s an example:

class PaymentGateway:
    def process_payment(self, amount):
        # process the payment

class Checkout:
    def __init__(self, amount):
        self.payment_gateway = PaymentGateway()
        self.amount = amount

    def process_checkout(self):
        self.payment_gateway.process_payment(self.amount)

In this example, the Checkout class creates an instance of the PaymentGateway class inside its constructor. This creates a tight coupling between the Checkout class and the PaymentGateway class. If you want to use a different payment gateway or mock the PaymentGateway class for testing purposes, you need to modify the code of the Checkout class.

With Dependency Injection, the Checkout class can receive an instance of the payment gateway via its constructor. Here’s an example:

class Checkout:
    def __init__(self, amount, payment_gateway):
        self.payment_gateway = payment_gateway
        self.amount = amount

    def process_checkout(self):
        self.payment_gateway.process_payment(self.amount)

In this example, the Checkout class receives an instance of the PaymentGateway class as a parameter in its constructor. This way, the Checkout class is decoupled from the PaymentGateway class, and we can easily switch to a different payment gateway or use a mock object for testing purposes.

Here’s an example of how we could use the Checkout class with Dependency Injection:

# Create an instance of the PaymentGateway class
payment_gateway = PaymentGateway()

# Create an instance of the Checkout class with the PaymentGateway object injected
checkout = Checkout(100, payment_gateway)

# Process the checkout
checkout.process_checkout()

In this example, we create an instance of the PaymentGateway class and inject it into an instance of the Checkout class. We can now call the process_checkout() method on the Checkout object and it will use the injected PaymentGateway object to process the payment.

Share Your Love
Avatar photo
Lingaraj Senapati

Hey There! I am Lingaraj Senapati, the Founder of lingarajtechhub.com My skills are Freelance, Web Developer & Designer, Corporate Trainer, Digital Marketer & Youtuber.

Articles: 411

Newsletter Updates

Enter your email address below to subscribe to our newsletter