Python dependency injection
When does dependency injection help in Python?
Dependency injection is useful when object construction becomes separate from application behavior. In Python, the simplest good option is often manual wiring. A container helps when that wiring spreads across many entrypoints.
Start with manual wiring
For a small script or one clear application entrypoint, direct constructor calls are readable and require no library. Python makes this easy, so a DI container should not be the default answer.
Use a small Python DI container when wiring repeats
A project usually outgrows manual wiring when the same repositories, clients, gateways, settings, and use cases are created in API startup, CLI commands, worker processes, and tests. Injex keeps that dependency graph in one composition root.
| Approach | Best when | Trade-off |
|---|---|---|
| Manual wiring | One entrypoint, few services. | Repeats as the app grows. |
| Framework DI | All dependency creation lives inside one framework. | Harder to reuse in CLIs and workers. |
| Injex | Shared service graph with typed constructors and tests. | Adds a small explicit container API. |
| Large DI container | Provider graphs, config DSLs, advanced composition. | More concepts and more surface area. |
Why typed injection matters
Injex uses normal constructor annotations as the dependency contract. Application services stay plain Python classes, while the container uses those annotations to resolve dependencies.
class Checkout:
def __init__(self, payments: PaymentGateway):
self.payments = payments
Where Injex fits
- Service layers that should not depend on FastAPI, Celery, or a CLI framework.
- Clean architecture use cases with repositories and gateways.
- Tests that need temporary fake services through
override(). - Startup checks that call
assert_valid()before serving traffic.
Frequently asked questions
Do Python applications need dependency injection?
Not by default. Small Python programs are often clearer with manual constructor calls. Dependency injection becomes useful when construction logic repeats or when tests need controlled replacement of external services.
When should I use a Python DI container?
Use one when a shared service graph should live in one composition root, dependencies are already expressed with type hints, and startup should catch missing registrations before real work begins.
How is Injex different from a provider framework?
Injex avoids a large provider DSL. It focuses on typed constructor injection, lifetimes, factories, named registrations, overrides, and validation while staying zero-dependency.
Practical rule: if manual wiring is clear, keep it. If the same graph appears in several entrypoints, move wiring into one explicit composition root.