Dependency Injection in Python: Ein Leitfaden mit der Injector-Bibliothek

Dependency Injection in Python: Ein Leitfaden mit der Injector-Bibliothek
Aktualisiert am 8.10.2024
Tobias Bück

Tobias Bück

Hallo! Ich bin Tobias Bück, Gründer von Softoft. Als Informatiker teile ich auf meinem Blog Einblicke in Softwareentwicklung, insbesondere das OTOBO Ticketsystem, sowie Automatisierungstools wie UIPath und Zapier.

In diesem Artikel erklären wir, wie du Dependency Injection in Python mit der Injector-Bibliothek umsetzt. Ziel ist es, die Vorteile von Dependency Injection zu erläutern und dir zu zeigen, wie du deinen Code durch den Einsatz von injector effizienter und wartungsfreundlicher gestalten kannst.

Was ist Dependency Injection in Python?

Dependency Injection (DI) ist ein Entwurfsmuster, das die Kopplung zwischen Klassen reduziert (Loose Coupling). Dies bedeutet, dass Klassen nicht mehr direkt miteinander arbeiten, sondern über Schnittstellen oder gemeinsame Abstraktionen kommunizieren. DI ermöglicht eine bessere Erweiterbarkeit und Testbarkeit des Codes.

Warum Dependency Injection?

Ohne Dependency Injection sind Klassen oft direkt an konkrete Implementierungen gebunden, was Änderungen erschwert und die Fehleranfälligkeit erhöht. DI löst dieses Problem, indem Abhängigkeiten bereitgestellt und verwaltet werden, anstatt sie direkt in der Klasse zu erstellen. In Python kann die injector-Bibliothek für dieses Vorgehen genutzt werden.

Dependency Injection in Python mit einem Wettervorhersage-System

Als Beispiel entwickeln wir ein kleines Wettervorhersage-System mit zwei verschiedenen Vorhersagern: "WeatherSatellite" und " WeatherStation". Beide Vorhersager implementieren die Wettervorhersage, jedoch wollen wir das System so gestalten, dass die konkrete Vorhersagemethode flexibel bleibt.

Beispiel ohne Dependency Injection

class WeatherSatellite:
    def get_temperature(self):
        return 20


class WeatherStation:
    def get_temperature(self):
        return 22


class Person:
    def __init__(self):
        self.weather_satellite = WeatherSatellite()

UML-Diagramm: Aktuelles Design ohne Dependency Injection

UML Diagramm: Beispiel ohne Dependency Injection
UML Diagramm: Beispiel ohne Dependency Injection

Problem: Die Person-Klasse entscheidet, welche Wettervorhersagemethode verwendet wird. Das führt zu einer starken Kopplung und erschwert es, die Implementierung zu ändern oder zu erweitern.

Verbesserung: Einführung von Interfaces

Python verwendet keine formalen Interfaces wie andere Sprachen, aber wir können ein gemeinsames Basisinterface oder eine Abstraktion durch Klassen schaffen. Hier definieren wir eine abstrakte Klasse IWeatherForecasting:

from abc import ABC, abstractmethod


class IWeatherForecasting(ABC):
    @abstractmethod
    def get_temperature(self):
        pass


class WeatherSatellite(IWeatherForecasting):
    def get_temperature(self):
        return 20


class WeatherStation(IWeatherForecasting):
    def get_temperature(self):
        return 22
class Person:
    def __init__(self, weather_forecasting: IWeatherForecasting):
        self.weather_forecasting = weather_forecasting

UML-Diagramm: Interface Implementierung ohne Dependency Injection

UML Diagramm: Interface
UML Diagramm: Interface

Problem: Obwohl jetzt ein Interface verwendet wird, entscheidet die Person-Klasse immer noch, welche Implementierung genutzt wird. Das widerspricht dem DI-Prinzip.

Lösung: Dependency Injection mit injector

Um diese Abhängigkeit vollständig zu lösen, verwenden wir die Bibliothek Injector, um die benötigten Objekte automatisch zu injizieren.

Code-Beispiel: Verwendung von Injector für Dependency Injection

from injector import Injector, inject, Module, singleton, provider


class WeatherModule(Module):
    @singleton
    @provider
    def provide_weather_forecasting(self) -> IWeatherForecasting:
        return WeatherSatellite()


class Person:
    @inject
    def __init__(self, weather_forecasting: IWeatherForecasting):
        self.weather_forecasting = weather_forecasting


def main():
    injector = Injector([WeatherModule()])
    person = injector.get(Person)
    print(person.weather_forecasting.get_temperature())


if __name__ == "__main__":
    main()

Erklärung

  • Module: Definiert, welche Implementierungen für Abhängigkeiten verwendet werden.
  • @provider: Gibt an, dass die Methode eine Abhängigkeit bereitstellt.
  • @inject: Ermöglicht die Injektion der Abhängigkeiten in den Konstruktor der Klasse.
  • injector.get: Erstellt automatisch eine Instanz von Person mit den notwendigen Abhängigkeiten.

UML-Diagramm: Design mit Dependency Injection

UML Diagramm: Dependency Container
UML Diagramm: Dependency Container

Vorteil

Die Person-Klasse ist nun vollständig unabhängig von der Implementierung der Wettervorhersage. Änderungen an der Implementierung können einfach in der WeatherModule-Klasse vorgenommen werden.

Erweiterung des DI-Containers

Es ist auch möglich, zusätzliche Abhängigkeiten oder Implementierungen zum Injector hinzuzufügen, indem man den Module-Klassen weitere @provider-Methoden hinzufügt.

Häufig gestellte Fragen zur Dependency Injection

Wie unterscheidet sich Dependency Injection in Python von C#?

Python ist dynamisch typisiert und verwendet keine formalen Interfaces. Stattdessen werden Abstraktionen oft über Basis- oder abstrakte Klassen implementiert. Bibliotheken wie injector ermöglichen jedoch ähnliche DI-Funktionen wie in statisch typisierten Sprachen wie C#.

Warum sollte ich injector statt manueller Dependency Injection verwenden?

Die Verwendung eines DI-Containers wie injector ermöglicht es, Abhängigkeiten zentral zu verwalten und macht den Code modularer und flexibler. Manuelles Verwalten von Abhängigkeiten ist fehleranfällig und erschwert die Erweiterung des Codes.

Fazit

Dependency Injection in Python mit der Bibliothek injector hilft, den Code modularer, flexibler und leichter erweiterbar zu gestalten. Durch die Verwendung eines DI-Containers können Abhängigkeiten automatisiert verwaltet werden, was die Implementierung einfacher und effizienter macht.

CookieFirst Logo

Wir verwenden Cookies

Wir können diese zur Analyse unserer Besucherdaten platzieren, um unsere Webseite zu verbessern, personalisierte Inhalte anzuzeigen und Ihnen ein großartiges Webseiten-Erlebnis zu bieten. Für weitere Informationen zu den von uns verwendeten Cookies öffnen Sie die Einstellungen.