[GO] Erstellen Sie einen DI-Container mit Python

Erstellen Sie einen DI-Container mit Python

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.

Gesamtbild

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.

  1. Wenn die Zielklasse in Singleton registriert ist, geben Sie die Instanz zunächst so zurück, wie sie ist.
  2. Überprüfen Sie als Nächstes, ob die Implementierungsklasse der Zielklasse in Container registriert ist.
  3. Wenn registriert, stellen Sie sicher, dass Sie eine Instanz der Implementierungsklasse erstellen.
  4. Wenn der Konstruktor der zu generierenden Klasse ein Argument benötigt, erstellen Sie zuerst eine Instanz der Argumentklasse. (Das heißt, lassen Sie die Argumentklasse von der Auflösungsfunktion generieren.)
  5. Nachdem Sie alle Instanzen der Argumentklasse mit Priorität auf Tiefe erstellt haben, übergeben Sie sie an das Argument der Zielklasse, um sie zu generieren.

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.

Zusammenfassung

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

Erstellen Sie einen DI-Container mit Python
Erstellen Sie eine Funktion in Python
Erstellen Sie ein Wörterbuch in Python
Erstellen Sie eine Binärdatei in Python
Erstellen Sie eine zufällige Zeichenfolge in Python
Erstellen Sie eine einfache GUI-App in Python
[GPS] Erstellen Sie eine kml-Datei mit Python
Erstellen Sie ein Python-Modul
Erstellen Sie Spatia Lite in Python
Erstellen Sie eine Python-Umgebung
Erstellen Sie in 1 Minute eine Vim + Python-Testumgebung
Erstellen Sie eine GIF-Datei mit Pillow in Python
Ich möchte mit Python ein Fenster erstellen
Erstellen Sie in Python ein Diagramm der Standardnormalverteilung
So erstellen Sie eine JSON-Datei in Python
Erstellen Sie eine virtuelle Umgebung mit conda in Python
Erstellen Sie in Python ein einfaches Momentum-Investmentmodell
Erstellen Sie eine neue Seite im Zusammenfluss mit Python
Erstellen Sie ein Datum / Uhrzeit-Objekt aus einer Zeichenfolge in Python (Python 3.3).
Erstellen Sie in Python ein Paket mit globalen Befehlen
Erstellen Sie eine MIDI-Datei in Python mit pretty_midi
Erstellen Sie in KiCad ein Rahmenantennenmuster in Python
[Docker] Erstellen Sie in 3 Minuten eine jupyterLab (Python) -Umgebung!
Machen Sie einen Screenshot in Python
Erstellen Sie ein Wox-Plugin (Python)
Erstellen Sie ein Lesezeichen in Python
Erstellen Sie ein Python-Numpy-Array
Zeichne ein Herz in Python
Erstellen Sie ein Verzeichnis mit Python
DI (Dependency Injection) in Python
Erstellen Sie mit Selenium einen Datenerfassungsbot in Python
[LINE Messaging API] Erstellen Sie mit Python ein umfangreiches Menü
Erstellen Sie ein Plug-In, das Python Doctest auf Vim ausführt (2)
Erstellen Sie ein Plug-In, um Python Doctest mit Vim (1) auszuführen.
Erstellen Sie in Python einen Dekorator, der Argumente dynamisch akzeptiert. Erstellen Sie einen Dekorator
Erstellen Sie mit Quarry einen gefälschten Minecraft-Server in Python
Wahrscheinlich in einer Nishiki-Schlange (Originaltitel: Vielleicht in Python)
Schreiben Sie eine Dichotomie in Python
[Python] Verwalten Sie Funktionen in einer Liste
Drücken Sie einen Befehl in Python (Windows)
Erstellen Sie mit Flask einen CSV-Reader
Erstellen Sie einen lokalen Bereich in Python, ohne den Namespace zu verschmutzen
Erstellen Sie mit tkinter eine Python-GUI
Erstellen Sie eine Python-Umgebung auf dem Mac (2017/4)
Zeichnen Sie eine Streudiagrammmatrix mit Python
Erstellen Sie ein untergeordnetes Konto für die Verbindung mit Stripe in Python
Erstellen wir ein Skript, das sich bei Ideone.com in Python registriert.
ABC166 in Python A ~ C Problem
Erstellen Sie eine virtuelle Umgebung mit Python!
Schreiben Sie A * (A-Stern) -Algorithmen in Python
Erstellen Sie Google Mail in Python ohne Verwendung der API
Löse ABC036 A ~ C mit Python
Erstellen Sie eine Python-Umgebung in Centos
Erstellen Sie den Code, der in Python "A und vorgeben B" ausgibt
Schreiben Sie ein Kreisdiagramm in Python
Schreiben Sie das Vim-Plugin in Python
Erstellen Sie eine Python-Projektdokumentation in Sphinx