» pip install dependency-injector
Once and for all, I want to learn the answer for this question, there are a lot of python developers arguing that DI is Java/C# thing only, but I also find people who said that they use DI in python and other dynamic languages (e.g. php). Did you ever used dependency injection in python (it would be good if you could provide example)? Does DI required in dynamic world? Should average python developer learn how to use it? What's in other dynamic languages (php, ruby etc) with DI?
In case anyone's interested, I've put together a DI framework "pylayer" in python that's fairly different from the alternatives I'm aware of (there aren't many). It includes a simple example at the bottom.
https://gist.github.com/johnhungerford/ccb398b666fd72e69f6798921383cb3f
What my project does
It allows you automatically construct dependencies based on their constructors.
The way it works is you define your dependencies as dataclasses inheriting from an Injectable class, where upstream dependencies are declared as dataclass attributes with type hints. Then you can just pass the classes to an Env object, which you can query for any provided type that you want to use. The Env object will construct a value of that type based on the Injectable classes you have provided. If any dependency needed to construct the queried type, it will generate an error message explaining what was missing and why it was needed.
Target audience
This is a POC that might be of interest to anyone who is uses or has wanted to use dependency injection in a Python project.
Comparison
https://python-dependency-injector.ets-labs.org/ is but complicated and unintuitive. pylayer is more automated and less verbose.
https://github.com/google/pinject is not maintained and seems similarly complicated.
https://itnext.io/dependency-injection-in-python-a1e56ab8bdd0 provides an approach similar to the first, but uses annotations to simplify some aspects of it. It's still more verbose and less intuitive, in my opinion, than pylayer.
Unlike all the above, pylayer has a relatively simple, functional mechanism for wiring dependencies. It is able to automate more by using the type introspection and the automated __init__ provided by dataclasses.
For anyone interested, my approach is based on Scala's ZIO library. Like ZIO's ZLayer type, pylayer takes a functional approach that uses memoization to prevent reconstruction of the same values. The main difference between pylayer and ZIO is that wiring and therefore validation is done at runtime. (Obviously compile-time validation isn't possible in Python...)
See Raymond Hettinger - Super considered super! - PyCon 2015 for an argument about how to use super and multiple inheritance instead of DI. If you don't have time to watch the whole video, jump to minute 15 (but I'd recommend watching all of it).
Here is an example of how to apply what's described in this video to your example:
Framework Code:
class TokenInterface():
def getUserFromToken(self, token):
raise NotImplementedError
class FrameworkClass(TokenInterface):
def do_the_job(self, ...):
# some stuff
self.user = super().getUserFromToken(...)
Client Code:
class SQLUserFromToken(TokenInterface):
def getUserFromToken(self, token):
# load the user from the database
return user
class ClientFrameworkClass(FrameworkClass, SQLUserFromToken):
pass
framework_instance = ClientFrameworkClass()
framework_instance.do_the_job(...)
This will work because the Python MRO will guarantee that the getUserFromToken client method is called (if super() is used). The code will have to change if you're on Python 2.x.
One added benefit here is that this will raise an exception if the client does not provide a implementation.
Of course, this is not really dependency injection, it's multiple inheritance and mixins, but it is a Pythonic way to solve your problem.
The way we do dependency injection in our project is by using the inject lib. Check out the documentation. I highly recommend using it for DI. It kinda makes no sense with just one function but starts making lots of sense when you have to manage multiple data sources etc, etc.
Following your example it could be something similar to:
# framework.py
class FrameworkClass():
def __init__(self, func):
self.func = func
def do_the_job(self):
# some stuff
self.func()
Your custom function:
# my_stuff.py
def my_func():
print('aww yiss')
Somewhere in the application you want to create a bootstrap file that keeps track of all the defined dependencies:
# bootstrap.py
import inject
from .my_stuff import my_func
def configure_injection(binder):
binder.bind(FrameworkClass, FrameworkClass(my_func))
inject.configure(configure_injection)
And then you could consume the code this way:
# some_module.py (has to be loaded with bootstrap.py already loaded somewhere in your app)
import inject
from .framework import FrameworkClass
framework_instance = inject.instance(FrameworkClass)
framework_instance.do_the_job()
I'm afraid this is as pythonic as it can get (the module has some python sweetness like decorators to inject by parameter etc - check the docs), as python does not have fancy stuff like interfaces or type hinting.
So to answer your question directly would be very hard. I think the true question is: does python have some native support for DI? And the answer is, sadly: no.
» pip install inject