DI Container ist ein System, das beim Erstellen einer Instanz einer Klasse automatisch eine Instanz des entsprechenden Typs an das Argument des Konstruktors übergibt. Sie können DI-Muster einfach implementieren und lose gekoppelten Code ausdrücken.
Wenn Sie mehr über DI-Muster erfahren möchten, lesen Sie bitte einen anderen Artikel. Eigentlich ist es sehr einfach zu machen, also werde ich mein Bestes geben.
Ich werde das fertige Repository verknüpfen. Repository
DEMO
Registrieren Sie in der DI-Klasse, dass dies die Implementierungsklasse ist oder dass diese Klasse in einer einzelnen Tonne verwendet wird. Danach muss die DI-Klasse aufgefordert werden, eine Instanz dieser Klasse zu erstellen.
Schau dir den Tintenfisch DEMO an.
class Animal:
def __init__(self, kind: str, name: str):
self.kind = kind
self.name = name
def __str__(self):
return "I am {kind}. My name is {name}".format(kind=self.kind, name=self.name)
class Cat(Animal):
def __init__(self, name: str = "Cathy"):
super().__init__('Cat', name)
di = DI()
di.register(Animal, Cat)
print(di.resolve(Animal))
# > I am Cat. My name is Cathy.
class Person:
def __init__(self, name: str, role: str):
self.name = name
self.role = role
def __str__(self):
return "I am {name}. My role is {role}.".format(name=self.name, role=self.role)
nukumizu = Person('Nukumizu', 'Actor')
di.register_singleton(Person, nukumizu)
print(di.resolve(Person))
# > I am Nukumizu. My role is Actor.
Die Mindestanforderungen für einen DI-Container sind die folgenden drei.
--Container-Klasse zum Registrieren der entsprechenden Klasse oder des Singleton --Resolver-Klasse, die registrierte Klassen usw. generiert.
Schauen wir uns den tatsächlichen Code der Reihe nach an.
Container Beginnen wir mit der Container-Klasse. Diese Klasse bietet die folgenden Funktionen:
Werfen wir einen Blick auf den tatsächlichen Code.
container.py
class DIContainer:
concrete_table = {}
singleton_table = {}
def register(self, base_cls: type, concrete_cls: type):
if not issubclass(concrete_cls, base_cls):
raise TypeError('Concrete class is required {} not {}.'.format(base_cls, concrete_cls))
self.concrete_table[base_cls] = concrete_cls
def register_singleton(self, t: type, instance: object):
if not isinstance(instance, t):
raise TypeError('Instance type is required {} not {}.'.format(t, type(instance)))
self.singleton_table[t] = instance
def get(self, t: type):
if self.is_registered_concrete(t):
return self.concrete_table[t]
return t
def get_singleton(self, t: type):
if self.is_registered_singleton(t):
return self.singleton_table[t]
raise KeyError('{} is not registered as singleton.'.format(t))
def is_registered_singleton(self, t: type):
return t in self.singleton_table.keys()
def is_registered_concrete(self, t: type):
return t in self.concrete_table.keys()
def is_registered(self, t: type):
return self.is_registered_concrete(t) or self.is_registered_singleton(t)
def clear(self):
self.concrete_table.clear()
self.singleton_table.clear()
Da ich mich nur ehrlich anmelde, gibt es meines Erachtens keinen besonderen Erklärungsort. Es tut mir leid, wenn der Code schwer zu lesen ist.
Resolver Schauen wir uns als nächstes die Resolver-Klasse an. Diese Klasse bietet die folgenden Funktionen:
Es gibt nur zwei.
Werfen wir einen Blick auf den tatsächlichen Code.
resolver.py
from container import DIContainer
class Resolver:
def __init__(self, container: DIContainer):
self.container = container
def resolve(self, cls: type):
if self.container.is_registered_singleton(cls):
return self.container.get_singleton(cls)
cls = self.container.get(cls)
init_args = self.resolve_init_args(cls)
return cls(**init_args)
def resolve_init_args(self, cls: type):
init_args_annotations = get_init_args_annotations(cls)
defaults = get_init_default_values(cls)
result = {}
args_count = len(init_args_annotations)
for key, t in init_args_annotations.items():
if self.container.is_registered(t) or len(defaults) < args_count:
result[key] = self.resolve(t)
else:
result[key] = defaults[len(defaults) - args_count]
args_count -= 1
return result
def get_init_args_annotations(cls: type):
if hasattr(cls.__init__, '__annotations__'):
return cls.__init__.__annotations__
return {}
def get_init_default_values(cls: type):
if hasattr(cls.__init__, '__defaults__'):
result = cls.__init__.__defaults__
return [] if result is None else result
return []
Ich werde get_init_args_annotations außerhalb der Klasse erklären. Alles, was Sie zum Erstellen eines DIContainers benötigen, sind die Konstruktorargumentinformationen. Ohne das geht es nicht. Der Weg, um die Konstruktorinformationen in Python abzurufen, ist annotations. Jetzt können Sie ein assoziatives Array mit dem Argumentnamen Key und dem Klassentyp Value abrufen. Ein Beispiel ist unten gezeigt.
class Person:
def __init__(self, name: str, age: int):
pass
print(Person.__init__.__annotaions__)
# {'name': <class 'str'> , 'age': <class 'int'>}
Erstellen Sie eine Instanz der Klasse basierend auf den Informationen, die durch \ __ Annotations__ erhalten wurden.
Lassen Sie uns nun den Ablauf des Generierungsprozesses überprüfen.
Es fühlt sich an, als würde man Klassen rekursiv generieren. Nachdem die Funktionen als DI-Container abgeschlossen sind, können Sie ihn verwenden, indem Sie eine Fassade erstellen.
di.py
from container import DIContainer
from resolver import Resolver
class DI:
def __init__(self):
self.container = DIContainer()
self.resolver = Resolver(self.container)
def register_singleton(self, t: type, instance: object):
self.container.register_singleton(t, instance)
def register(self, base_cls: type, concrete_cls: type):
self.container.register(base_cls, concrete_cls)
def resolve(self, t: type):
return self.resolver.resolve(t)
def clear(self):
self.container.clear()
Zu diesem Zeitpunkt können Sie DI Container wie DEMO verwenden.
Sobald Sie es verstanden haben, werden Sie weniger wahrscheinlich vergessen, wie man es benutzt. Da ich das DI-Muster kannte, löste ich einige Fragen zum Umgang mit Schnittstellenbeziehungen.
Es war wahnsinnig praktisch, nicht überprüfen zu müssen, wie Klassen einzeln generiert werden, indem DI Container am vorherigen Arbeitsplatz in unsere eigene Bibliothek eingeführt wird. Natürlich gibt es Nachteile, aber es ist immer noch praktisch, also werde ich es verwenden, wenn es verwendet werden kann.
Ich werde das fertige Repository verknüpfen. Repository
Ich würde mich freuen, wenn Sie mich über Mängel informieren könnten.
Vielen Dank, dass Sie bis zum Ende bei uns bleiben.
Recommended Posts