Jason Mathnov
Jason Mathnov

Reputation: 99

Python dependency injection

I have 3 modules: A, B, C

A contains a set of classes that B has fetchers for.

B contains a bunch of singletons that just deal with caching created objects and providing them when requested. Essentially just fetchers.

C is a list of functions that requires instances of A. 

The operation that I need to do is something along the lines of:

C::SomeFunc():
   B.getInstance("instance ID")


B::getInstance(someID: str) -> A:
   -look at cache, which is either [] or {}
   -if it is in cache, return that, else do:  A(someID)

My question is, how would you pass around the instances of these modules around? This question is primarily motivated by my lack of understanding of Python's memory allocation system.

I could do something along the lines of constructor-based dependency injection to get an instance of A,B,C where they need to go, and then have some "master/god/controller" object that just passes things where they need to go -

eg:

class god(object):
    def __init__(self):
        a = A()
        b = B()
        c = C(b)
.....
.....
class C(object):
    def __init__(self, B_instance):
         self.B = B_instance
    def SomeFunc(self, instanceID):
         self.B.getInstance(instanceID)

but this seems like a hack.

Any suggestions?

Thanks!

Upvotes: 1

Views: 2874

Answers (3)

Rodrigo Oliveira
Rodrigo Oliveira

Reputation: 1582

You can use injectable for that. It's a dependency injection autowiring micro-framework.

This is what your code would look like:

@injectable(singleton=True)
class MySingleton:
    ...
@autowired
def func_that_needs_my_singleton(my_singleton: Autowired(MySingleton)):
    ...

Upvotes: 2

I recently released a small library that might help you achieve this. Feedback is very welcome :D

https://github.com/suned/serum

Upvotes: 1

Aigrefin
Aigrefin

Reputation: 125

I've seen some packages that offer an instance provider on there on PyPI if that's what you are looking for. Personally, I didn't want to deal with another object to obtain instances. So I created my own library for this (Python 3 only). You only need @inject and annotations.

from py3njection import inject
from some_package import ClassToInject

class Demo:
    @inject
    def __init__(self, object_to_use: ClassToInject):
        self.dependency = object_to_use

demo = Demo()

You can find it here. It basically makes a new instance each time the method is called.

Testing is easy with this since you only have to pass mocks as arguments to unittest your function/method.

I've added @singleton too.

If you need even more instantiation ways, like the cache system you're describing, the docs explain how to easily implement your own. Everything cache related would be taken care of in a factory, and the objects that depends on them wouldn't have to know how to get them.

But if you're on Python 2 or if you need to have more control on injections, this could work too. But you still have to manipulate a provider.

Upvotes: 3

Related Questions