What is a Pythonic way for Dependency Injection?

bagrat picture bagrat · Jul 28, 2015 · Viewed 31k times · Source

Introduction

For Java, Dependency Injection works as pure OOP, i.e. you provide an interface to be implemented and in your framework code accept an instance of a class that implements the defined interface.

Now for Python, you are able to do the same way, but I think that method was too much overhead right in case of Python. So then how would you implement it in the Pythonic way?

Use Case

Say this is the framework code:

class FrameworkClass():
    def __init__(self, ...):
        ...

    def do_the_job(self, ...):
        # some stuff
        # depending on some external function

The Basic Approach

The most naive (and maybe the best?) way is to require the external function to be supplied into the FrameworkClass constructor, and then be invoked from the do_the_job method.

Framework Code:

class FrameworkClass():
    def __init__(self, func):
        self.func = func

    def do_the_job(self, ...):
        # some stuff
        self.func(...)

Client Code:

def my_func():
    # my implementation

framework_instance = FrameworkClass(my_func)
framework_instance.do_the_job(...)

Question

The question is short. Is there any better commonly used Pythonic way to do this? Or maybe any libraries supporting such functionality?

UPDATE: Concrete Situation

Imagine I develop a micro web framework, which handles authentication using tokens. This framework needs a function to supply some ID obtained from the token and get the user corresponding to that ID.

Obviously, the framework does not know anything about users or any other application specific logic, so the client code must inject the user getter functionality into the framework to make the authentication work.

Answer

Serban Teodorescu picture Serban Teodorescu · Aug 9, 2015

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.