DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • SwiftData Dependency Injection in SwiftUI Application
  • Why Use AWS Lambda Layers? Advantages and Considerations
  • Tornado vs. FastAPI: Why We Made the Switch
  • Essential Protocols for Python Developers to Prevent SQL Injection Attacks

Trending

  • *You* Can Shape Trend Reports: Join DZone's Software Supply Chain Security Research
  • Beyond Linguistics: Real-Time Domain Event Mapping with WebSocket and Spring Boot
  • Microsoft Azure Synapse Analytics: Scaling Hurdles and Limitations
  • Mastering Fluent Bit: Installing and Configuring Fluent Bit on Kubernetes (Part 3)
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Testing, Tools, and Frameworks
  4. Why I Started Using Dependency Injection in Python

Why I Started Using Dependency Injection in Python

Learn how Dependency Injection (DI) in Python helps improve code structure, testing, and flexibility, using real-world examples and the dependency-injector library.

By 
Haymang Ahuja user avatar
Haymang Ahuja
·
Updated by 
Saransh Arora user avatar
Saransh Arora
·
Apr. 28, 25 · Analysis
Likes (1)
Comment
Save
Tweet
Share
2.7K Views

Join the DZone community and get the full member experience.

Join For Free

When I first started building real-world projects in Python, I was excited just to get things working. I had classes calling other classes, services spun up inside constructors, and everything somehow held together.

But deep down, I knew something was off.

Why?

My code felt clunky. Testing was a nightmare. Changing one part broke three others. Adding a small feature would make me change 10 different files in the package. I couldn't quite explain why - until a seasoned software developer reviewed my code and asked, "Have you heard of dependency injection?"

I hadn't.

So I went down the rabbit hole. And what I found changed the way I think about structuring code. In this post, I'll walk you through what dependency injection is, how it works in Python, and why you might want to start using a library like dependency-injector. We'll use real-world examples, simple language, and practical code to understand the concept.

A yummy photo of spaghetti

If you're early in your dev journey or just looking to clean up messy code, this one's for you.

So, What the Heck Is Dependency Injection?

Let's break it down.

Dependency injection (DI) is just a fancy term for "give me what I need, don't make me create it myself."

Instead of a class or function creating its own dependencies (like other classes, database, clients, etc), you inject those dependencies from the outside. This makes it easier to swap them out, especially in tests, and promotes loose coupling.

A Quick Real-World Analogy 

Imagine you're running a coffee shop. Every time someone orders coffee, you don't go build a coffee machine from scratch. You already have one, and you pass it to the barista.

That's DI. The barista (your class) doesn't create the coffee machine (the dependency to serve coffee); it receives it from outside.

Without DI: A Painful Example

Python
 
class DataProcessor:
    def __init__(self):
        # DataProcessor creates its own database connection
        self.db = MySQLDatabase(host="localhost", user="root", password="secret")

    def process_data(self):
        data = self.db.query("SELECT * from data")
        # Process the data...
        return processed_data


See the problem?

This works, but here's the problem. Our DataProcessor is tightly coupled to a specific MySQL database implementation.

  • Want to switch databases? You'll have to modify this class.
  • Want to test it with a mock database? Good luck with that.

With DI: Clean and Flexible

Now, here's the same code with dependency injection.

Python
 
class DataProcessor:
    def __init__(self, database):
        # Database is injected from outside
        self.db = database

    def process_data(self):
        data = self.db.query("SELECT * from data")
        # Process the data ...
        return processed_data

# Creating and injecting dependencies
db = MySQLDatabase(host="localhost", user="root", password="secret")
processor = DataProcessor(db)


That's it! The processor now takes its database dependency as a parameter instead of creating it internally. This simple change makes a world of difference.

Using a DI Library in Python

The simplest form of DI in Python is just passing dependencies through constructors (as we saw above). This is called constructor injection and works great for simple cases. But in larger applications, manually writing dependencies gets messy.

That's where the dependency-injector library shines. It gives you containers, providers, and a nice way to manage your dependencies.

First, let's install the package:

Plain Text
 
pip install dependency-injector


Now, let's see it in action in the real world. Imagine we're building a data engineering pipeline that:

  1. Extracts data from different sources.
  2. Transforms it.
  3. Loads it into a data warehouse.

Here's how we'd structure it with DI:

Python
 
from dependency-injector import containers, providers
from database import PostgresDatabase, MongoDatabase
from services import DataExtractor, DataTransformer, DataLoader

class Container(containers.DeclarativeContainer):
    config = providers.Configuration()

    # Database dependencies
    postgres_db = providers.Singleton(
        PostgresDatabase,
        host=config.postgres.host,
        username=config.postgres.username,
        password=config.postgres.password
    )

    mongo_db = providers.Singleton(
        MongoDatabase,
        connection_string=config.mongo.connection_string
    )

    # Service dependencies
    extractor = providers.Factory(
        DataExtractor,
        source_db=mongo_db
    )

    transformer = providers.Factory(
        DataTransformer
    )

    loader = providers.Factory(
        DataLoader,
        target_db=postgres_db
    )

    # Main application
    etl_pipeline = providers.Factory(
        ETLpipeline,
        extractor=extractor,
        transfomer=transformer,
        loader=loader
    )


Let's break down what's happening here:

Containers

A container is like a registry of your application's dependencies. It knows how to create each component and what dependencies each component needs.

Providers

Providers tell the container how to create objects. There are different types:

  • Factory: Creates a new instance each time (good for most services)
  • Singleton: Creates only one instance (good for connections, for example)
  • Configuration: Provides configuration values
  • And many more... (check out the complete list following the link above)

Using the Container

Python
 
# Load configuration from a file
container = Container()
container.config.from_yaml('config.yaml')

# Create the pipeline with all dependencies resolved automatically
pipeline = container.etl_pipeline()
pipeline.run()


The magic here is that our ETLPipeline class doesn't need to know how to create extractors, transformers, or loaders. It just uses what it's given:

Python
 
class ETLPipeline:
    def __init__(self, extractor, transformer, loader):
        self.extractor = extractor
        self.transformer = transformer
        self.loader = loader

    def run(self):
        data = self.extractor.extract()
        transformed_data = self.transformer.transform(data)
        self.loader.load(transformed_data)


Wrapping Up

Dependency injection might sound like something only big enterprise apps need. But if you've ever tried to write tests or refactor your code and wanted to scream, DI can save your sanity.

The dependency-injector library in Python gives you an elegant way to structure your code, keep things loosely coupled, and actually enjoy testing.

Final Thoughts

Next time you're writing a class that needs "stuff" to work, pause and ask: Should I be injecting this instead?

It's a simple shift in mindset, but it leads to cleaner code, better tests, and happier developers (you included).

If you found this helpful, give it a clap or share it with a teammate who's knee-deep in spaghetti code. And if you want more articles like this — real talk, real examples — follow me here on DZone.

Let's write better Python, one clean service at a time.

Dependency injection Dependency Injection Python (language)

Opinions expressed by DZone contributors are their own.

Related

  • SwiftData Dependency Injection in SwiftUI Application
  • Why Use AWS Lambda Layers? Advantages and Considerations
  • Tornado vs. FastAPI: Why We Made the Switch
  • Essential Protocols for Python Developers to Prevent SQL Injection Attacks

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!