Dependency Injection in Python



Dependency Injection is a very good mechanism of implementing loose couplings, and it helps make our application maintainable and extendable. Combine it with Duck Typing and the Force will be with you always.

Python offers us all we need to implement that easily. Think about its possible implementation in other languages such as Java and C#, and you'll quickly realize the beauty of Python.

Let's think about a simple example of dependency injection:

                    class Command:

                        def __init__(self, authenticate=None, authorize=None):
                            self.authenticate = authenticate or default_authenticate
                            self.authorize = authorize or default_autorize

                        def execute(self, user, action):
                            self.authenticate(user)
                            self.authorize(user, action)
                            return action()

                    if in_sudo_mode:
                        command = Command(always_authenticated, always_authorized)
                    else:
                        command = Command(config.authenticate, config.authorize)
                        command.execute(current_user, delete_user_action)
                

We inject the authenticator and authorizer methods in the Command class. All the Command class needs is to execute them successfully without bothering with the implementation details. This way, we may use the Command class with whatever authentication and authorization mechanisms we decide to use in runtime.

We have shown how to inject dependencies through the constructor, but we can easily inject them by setting directly the object properties, unlocking even more potential:

                    command = Command()

                    if in_sudo_mode:
                        command.authenticate = always_authenticated
                        command.authorize = always_authorized
                    else:
                        command.authenticate = config.authenticate
                        command.authorize = config.authorize
                        command.execute(current_user, delete_user_action)
                

There is much more to learn about dependency injection. Again, we just demonstrated how implementing this wonderful design pattern in Python is just a matter of using the built-in functionalities of the language.

Let's not forget what all this means: The dependency injection technique allows for very flexible and easy unit-testing. If you are building classes to be used in multiple applications then Dependency Injection is a good choice.