ivankorobkov / python-inject

Python dependency injection
Apache License 2.0
672 stars 77 forks source link

Problem of binding a constructor which is a member function #44

Closed sisai closed 4 years ago

sisai commented 4 years ago

Dear authors, Thank you for your hard working on the python-inject package. I am new to the concept of dependency injection, and I am trying to use this idea and the python-inject package in my current project. I encountered a problem that I cannot solve after hours. My problem is as follows: Suppose that we have two classes A and B, B's init function is dependent on A:

class A(object):
    def __init__(self):
        print('A')

class B(object):
    @inject.autoparams()
    def __init__(self, a: A):
        self._a = a
        print('B')

Then, to create the injection

def config():
    binder.bind_to_constructor(A, A.__init__)
    binder.bind_to_constructor(B, B.__init__)

inject.configure(config)

Running this code gives an error 

"File "...\lib\site-packages\inject-4.0.0-py3.6.egg\inject__init.py", line 201, in call self._instance = self._constructor() TypeError: init__() missing 1 required positional argument: 'self'".

I know there is an alternative way to implement this, which is manually instantiate an instance of A, and then bind dependencies on A to that instance. But what if I have another class C, whose instantiation is dependent on B:

class C(object):
    @inject.autoparams()
    def __init__(self, b: B):
        self._b = b
        print('C')

Then how to create the dependencies, 

def config():
    a = A()
    binder.bind(A, a)
    binder.bind(B, ???)
ivankorobkov commented 4 years ago

Hi!

You should bind to the class itself, i.e.:

def config():
    binder.bind_to_constructor(A, A)
    binder.bind_to_constructor(B, B)

In Python calling A or B internally results in two different calls. First, the class instantiates an instance via class.__new__ and then passes it to an initializing constructor class.__init__. See https://docs.python.org/3/reference/datamodel.html

Classes are callable. These objects normally act as factories for new instances of themselves, but variations are possible for class types that override new(). The arguments of the call are passed to new() and, in the typical case, to init() to initialize the new instance.