Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can Python do DI seamlessly without relying on a service locator?

I'm coming from the C# world, so my views may be a little skewed. I'm looking to do DI in Python, however I'm noticing a trend with libraries where they all appear to rely on a service locator. That is, you must tie your object creation to the framework, such as injectlib.build(MyClass) in order to get an instance of MyClass.

Here is an example of what I mean -

from injector import Injector, inject


class Inner(object):
    def __init__(self):
        self.foo = 'foo'


class Outer(object):
    @inject(inner=Inner)
    def __init__(self, inner=None):
        if inner is None:
            print('inner not provided')
            self.inner = Inner()
        else:
            print('inner provided')
            self.inner = inner


injector = Injector()

outer = Outer()
print(outer.inner.foo)

outer = injector.get(Outer)
print(outer.inner.foo)

Is there a way in Python to create a class while automatically inferring dependency types based on parameter names? So if I have a constructor parameter called my_class, then an instance of MyClass will be injected. Reason I ask is that I don't see how I could inject a dependency into a class that gets created automatically via a third party library.

like image 408
self. Avatar asked Oct 07 '14 18:10

self.


1 Answers

To answer the question you explicitly asked: no, there's no built-in way in Python to automatically get a MyClass object from a parameter named my_class.

That said, neither "tying your object creation to the framework" nor the example code you gave seem terribly Pythonic, and this question in general is kind of confusing because DI in dynamic languages isn't really a big deal.

For general thoughts about DI in Python I'd say this presentation gives a pretty good overview of different approaches. For your specific question, I'll give two options based on what you might be trying to do.

If you're trying to add DI to your own classes, I would use paramaters with default values in the constructor, as that presentation shows. E.g:

import time

class Example(object):
    def __init__(self, sleep_func=time.sleep):
        self.sleep_func = sleep_func

    def foo(self):
        self.sleep_func(10)
        print('Done!')

And then you could just pass in a dummy sleep function for testing or whatever.

If you're trying to manipulate a library's classes through DI, (not something I can really imagine a use case for, but seems like what you're asking) then I would probably just monkey patch those classes to change whatever needed changing. E.g:

import test_module

def dummy_sleep(*args, **kwargs):
    pass

test_module.time.sleep = dummy_sleep
e = test_module.Example()
e.foo()
like image 65
R Phillip Castagna Avatar answered Nov 04 '22 11:11

R Phillip Castagna