Open javierseixas opened 1 month ago
Hello.
First, start by configuring an injector. Usually, it is done at the start of your application.
inject.configure(my_config)
Specify optional providers in the config, or let inject
automatically construct singletons for you:
def my_config(binder):
binder.bind(Cache, RedisCache('localhost:1234'))
binder.bind_to_provider(DB, SQLDatabase)
Use inject
to inject instances:
class User:
db = inject.attr(DB)
@classmethod
def get_by_id(cls, id):
row = cls.db.fetch('users', id)
return ...
Use other ways to inject dependencies, i.e. inject.autoparams
, etc.
--
See something like https://stackoverflow.com/questions/130794/what-is-dependency-injection to better understand concepts.
Thanks for your response, it's the same that appears in the README, though :sweat_smile:
Can I build a decorator pattern using inject? This pattern uses the same interface in several classes, so for instance, I have this interface:
from abc import ABC, abstractmethod
class Interface(ABC):
@abstractmethod
def say_hello(self):
pass
This class implementing the previous interface:
import inject
class Decorator(Interface):
decorated: Decorated = inject.attr(Decorated)
def say_hello(self):
print("Hello")
self.decorated.say_hello()
And this other implementation of the interface:
class Decorated(Interface):
def say_hello(self):
print("Hello World!")
Having that, when I set my config like this:
def my_config(binder):
binder.bind(Interface, Decorator())
binder.bind(Interface, Decorated())
I get this error: inject.InjectorException: Duplicate binding, key=<class 'interface.Interface'>
What I expect from your examples is that I can define several singletons based on the same Interface: Cache
is the interface and RedisCache
its implementation.
Anyhow, how I could implement decorator or strategy patterns, where I can have more than one singleton implementing the same interface?
Thanks for your help.
Anyhow, how I could implement decorator or strategy patterns, where I can have more than one singleton implementing the same interface?
Well, you can't.
The injector gives you the exact error here:
binder.bind(Interface, Decorator())
binder.bind(Interface, Decorated())
You can't bind multiple values to the same type.
What instance, Decorator
or Decorated
, should be injected here?
@inject.param(Interface)
def function(iface):
pass
I see your point. So, answering my initial question: the way of doing this that comes to my mind would be something injecting the configuration, like this:
import inject
from abc import ABC, abstractmethod
class Interface(ABC):
@abstractmethod
def say_hello(self):
pass
class NerdDecorator(Interface):
def say_hello(self):
print("Hello World!")
class FrenchDecorator(Interface):
decorated: Interface = inject.attr("nerd")
def say_hello(self):
print("Salut!")
self.decorated.say_hello()
class EnglishDecorator(Interface):
decorated: Interface = inject.attr("french")
def say_hello(self):
print("Hello")
self.decorated.say_hello()
class CatalanDecorator(Interface):
decorated: Interface = inject.attr("english")
def say_hello(self):
print("Hola")
self.decorated.say_hello()
def my_config(binder):
binder.bind("catalan", CatalanDecorator())
binder.bind("english", EnglishDecorator())
binder.bind("french", FrenchDecorator())
binder.bind("nerd", NerdDecorator())
pass
inject.configure(my_config)
decorator = CatalanDecorator()
decorator.say_hello()
Resulting on:
Hola
Hello
Salut!
Hello World!
So I'm manually creating the singletons and indicating where to inject them. Is there a better way you may suggest, @ivankorobkov ?
Yes, you are right.
I would also mention that it's better to use types, not strings. I think it is easier to reason about the application structure as a typed object graph.
Also using types usually allows to omit binding specifications.
class NerdDecorator(Interface):
def say_hello(self):
print("Hello World!")
class FrenchDecorator(Interface):
decorated: Interface = inject.attr(NerdDecorator)
def say_hello(self):
print("Salut!")
self.decorated.say_hello()
class EnglishDecorator(Interface):
decorated: Interface = inject.attr(FrenchDecorator)
def say_hello(self):
print("Hello")
self.decorated.say_hello()
class CatalanDecorator(Interface):
decorated: Interface = inject.attr(EnglishDecorator)
def say_hello(self):
print("Hola")
self.decorated.say_hello()
def my_config(binder):
pass
inject.configure(my_config)
decorator = CatalanDecorator()
decorator.say_hello()
also prints:
Hola
Hello
Salut!
Hello World!
Thanks for all your explanations. What do you think about including some more samples in the README? Can I propose a PR with that?
Sure, I you make a PR, I will merge it.
Hello! I'm interested in using this library, but it's being difficult to understand how it works, just reading the README file. Is there more information or samples so I can understand better how configure the container.
Thanks.