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
Spring - Dependency Injection with Factory Method
Spring framework provides Dependency Injection to remove the conventional dependency relationship between objects. To inject dependencies using the factory method, we will use two attributes factory-method and factory-bean of bean elements. Note: Factory methods are those methods that return the ins
8 min read
Dependency Injection vs Factory Pattern
In coding, there are special ways to organize our work called "design patterns." Two important ones are Dependency Injection (DI) and the Factory Pattern. They help make our work easier by keeping things neat and separate. In this article, we will see the differences between them and, when to use ea
4 min read
Why to Use the Dependency Injection Framework like Dagger in Android?
In our project, we may or may not have used a dependency framework. In this blog, we will discuss why we need a dependency framework and why using one in our project is beneficial. We can assume that this is an interview question in this case. In this Geeks for Geeks article, we will respond to it b
5 min read
What is Python? Its Uses and Applications
Python is a programming language that is interpreted, object-oriented, and considered to be high-level. What is Python? Python is one of the easiest yet most useful programming languages and is widely used in the software industry. People use Python for Competitive Programming, Web Development, and
8 min read
Spring - Dependency Injection by Setter Method
Dependency Injection is one of the core features of the Spring Framework Inversion of Control (IOC) container. It reduces the need for classes to create their own objects by allowing the Spring IOC container to do it for them. This approach makes the code more flexible, easier to test, and simpler t
5 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
Spring - Setter Injection with Dependent Object
Dependency Injection is the main functionality provided by Spring IOC(Inversion of Control). The Spring-Core module is responsible for injecting dependencies through either Constructor or Setter methods. In Setter Dependency Injection(SDI) the dependency will be injected with the help of setters and
3 min read
Introduction to Python for Absolute Beginners
Are you a beginner planning to start your career in the competitive world of Programming? Looking resources for Python as an Absolute Beginner? You are at the perfect place. This Python for Beginners page revolves around Step by Step tutorial for learning Python Programming language from very basics
6 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
Updating dependencies in Python Poetry
Updating dependencies in Python Poetry is straightforward and efficient, thanks to its comprehensive command-line interface and powerful dependency resolution capabilities. Regularly updating your dependencies ensures that your project benefits from the latest features, bug fixes, and security impro
3 min read