What is a Pythonic Way for Dependency Injection?
Last Updated :
15 Jul, 2024
Design pattern Dependency Injection (DI) achieves Inversion of Control (IoC) between classes and their dependencies. DI increases testability and modularity by externalizing the building and control of dependencies. This post will investigate DI from a Pythonic standpoint with an eye toward best practices and pragmatic solutions.
Understanding Dependency Injection:
Dependency Injection involves injecting a class's dependencies rather than letting the class generate them on its own. This facilitates easy code management and testing and loose coupling.
Benefits of Dependency Injection:
- Modularity: Separates the building of dependencies from their use, therefore encouraging a modular codebase.
- Testability: Testability lets one easily substitute dependencies with mocks or stubs, therefore facilitating testing.
- Maintainability: By lowering close dependency between components, maintainability of the code simplifies upgrading and maintenance of it.
Principles of Pythonic Dependency Injection
Pythonic Design Patterns
Pythonic design patterns stress simplicity, readability, and the application of built-in language tools. In the context of DI, this implies using Python's dynamic character and simple syntax to apply DI neatly and effectively.
Writing Clean and Readable Code
Follow these ideas to guarantee DI implementations are Pythonic:
- Explicit is superior to implicit: Specify exactly where and how dependencies are introduced.
- Simple is better than complicated. Steer clear of too ambitious DI solutions.
- Count of readable words: Create easily readable and understandable codes for others.
Dependency Injection Techniques in Python
Constructor Injection
Constructor Injection is supplying dependencies via a class's initializer, __init__ method.
Python
class Service:
def do_something(self):
print("Service is doing something")
class Client:
def __init__(self, service: Service):
self.service = service
def perform_task(self):
self.service.do_something()
# Dependency Injection
service = Service()
client = Client(service)
client.perform_task()
OutputService is doing something
Setter Injection
Setter Injection lets dependencies be injected via a process following object creation.
Python
class Service:
def do_something(self):
print("Service is doing something")
class Client:
def __init__(self):
self.service = None
def set_service(self, service: Service):
self.service = service
def perform_task(self):
self.service.do_something()
# Dependency Injection
service = Service()
client = Client()
client.set_service(service)
client.perform_task()
OutputService is doing something
Method Injection
Method injection is forwarding dependencies straight to the required method.
Python
class Service:
def do_something(self):
print("Service is doing something")
class Client:
def perform_task(self, service: Service):
service.do_something()
# Dependency Injection
service = Service()
client = Client()
client.perform_task(service)
OutputService is doing something
Using Python Libraries for Dependency Injection
Overview of Popular Libraries
Each providing unique features and capabilities, several libraries can assist control dependency injection in Python programmes.
Dependency Injector
Popular library Dependency Injector gives a complete framework for DI in Python.
Python
pip install dependency-injector
from dependency_injector import containers, providers
class Service:
def do_something(self):
print("Service is doing something")
class Client:
def __init__(self, service: Service):
self.service = service
def perform_task(self):
self.service.do_something()
class Container(containers.DeclarativeContainer):
service = providers.Singleton(Service)
client = providers.Factory(Client, service=service)
container = Container()
client = container.client()
client.perform_task()
Service is doing something
Flask-Injector
By combining DI with the Flask web platform, Flask-Injector simplifies web application dependability management.
Python
pip install flask flask-injector
from flask import Flask
from flask_injector import FlaskInjector
from injector import inject, singleton
class Service:
def do_something(self):
print("Service is doing something")
class Client:
@inject
def __init__(self, service: Service):
self.service = service
def perform_task(self):
self.service.do_something()
def configure(binder):
binder.bind(Service, to=Service, scope=singleton)
app = Flask(__name__)
@app.route('/')
def index(client: Client):
client.perform_task()
return 'Task performed'
FlaskInjector(app=app, modules=[configure])
if __name__ == '__main__':
app.run()
Service is doing something
Wiring with Pydantic
Dependency wiring among other configuration validation and management tools may be found in Pydantic.
Python
!pip install pydantic
# Writing the .env file
with open('.env', 'w') as f:
f.write('db_url=your_database_url\n')
f.write('api_key=your_api_key\n')
from pydantic import BaseSettings
class Settings(BaseSettings):
db_url: str
api_key: str
# Load settings from the .env file
settings = Settings(_env_file='.env')
# Print the settings
print(settings.db_url)
print(settings.api_key)
your_database_url
your_api_key
Implementing Dependency Injection in Python
Step-by-step execution:
- First define the services and their interfaces to then define the dependencies.
- Apply the DI Container method. Design a container class to handle dependant wiring and instantiation.
- Dependencies on injections: Add dependencies into the client classes via constructor, setter, or method injection.
Practical Examples and Use Cases
Example-1: Web Application
Python
class Database:
def connect(self):
print("Connecting to the database...")
class Service:
def __init__(self, database: Database):
self.database = database
def execute(self):
self.database.connect()
print("Service is executing...")
class Application:
def __init__(self, service: Service):
self.service = service
def run(self):
self.service.execute()
# Dependency Injection
database = Database()
service = Service(database)
app = Application(service)
app.run()
OutputConnecting to the database...
Service is executing...
Example 2: CLI Tool
Python
class Logger:
def log(self, message):
print(message)
class Task:
def __init__(self, logger: Logger):
self.logger = logger
def run(self):
self.logger.log("Task is running")
def main():
logger = Logger()
task = Task(logger)
task.run()
if __name__ == '__main__':
main()
Conclusion
Powerful for encouraging modularity, testability, and maintainability in your Python code is dependency injection. Using suitable libraries and applying Pythonic ideas will help you to develop DI in a neat and effective way.
Similar Reads
private vs private-final injection of dependency Dependency Injection (DI) is essential for improving code modularity, maintainability, and testability in the context of sophisticated Java programming. Choosing between private and private-final injection for dependencies is a typical DI concern. We will examine the distinctions between these two s
5 min read
Dependency tree of a Python Module Generally, many Python packages are dependent on other packages but how do we know that on which packages is a module dependent? pip freeze: This is a python built-in module that can help us know the dependent packages but it shows all dependencies as a flat list, finding out which are the top-level
7 min read
Best Practices for Managing Python Dependencies Python is one of the most popular programming languages, largely due to its simplicity, flexibility, and rich ecosystem of third-party packages. However, as projects become complex, managing Python dependencies effectively becomes crucial to ensure consistent development environments and avoid confl
6 min read
Dependency Injection in Distributed Systems Dependency Injection (DI) is a powerful design pattern used to manage dependencies between components in software systems. In distributed systems, where components are distributed across various nodes or services, the complexity of managing dependencies increases. DI can significantly enhance modula
8 min read
What is a clean and Pythonic way to have multiple constructors in Python? Python does not support explicit multiple constructors, yet there are some ways to achieve multiple constructors. We use Python's inbuilt __init__ method to define the constructor of a class. It tells what will the constructor do if the class object is created. If multiple __init__ methods are writt
7 min read
Dependency Injection in FastAPI This article explores Dependency Injection (DI) in FastAPI, a modern Python web framework for building APIs. FastAPI's versatility is evident when there's a need to reuse code or override specific sections. Dependency injection in FastAPI facilitates these tasks seamlessly, and we'll delve into its
5 min read
Spring Boot - Dependency Injection and Spring Beans Spring Boot is a powerful framework for building RESTful APIs and microservices with minimal configuration. Two fundamental concepts within Spring Boot are Dependency Injection (DI) and Spring Beans. Dependency Injection is a design pattern used to implement Inversion of Control (IoC), allowing the
6 min read
Managing dependencies with Python Poetry Managing dependencies in Python projects can be a challenging task, especially as projects grow in size and complexity. Poetry is a powerful tool designed to simplify dependency management, packaging, and publishing for Python projects. In this article, weâll explore what Poetry is, its features, an
3 min read
Defining a Python Function at Runtime One amazing feature of Python is that it lets us create functions while our program is running, instead of just defining them beforehand. This makes our code more flexible and easier to manage. Itâs especially useful for things like metaprogramming, event-driven systems and running code dynamically
3 min read
Using Poetry Dependency Management tool in Python In this article, we are going to study the poetry dependency management tool in python which will help you to manage the libraries of your next project so it will be easy to download, install, and set up your project. What is Poetry Poetry is a python dependency management tool to manage dependencie
4 min read