Dependency Injection in Python
Lucien Chemaly
31. Oktober 2023
0 Min. LesezeitDependency Injection (DI) klingt vielleicht kompliziert, aber es geht einfach darum, Ihren Code flexibler zu machen. So wird Ihre Software anpassungsfähiger und robuster. Da Python eine beliebte Programmiersprache ist, lohnt es sich besonders, Dependency Injection zu verstehen. Stellen Sie sich Software vor, die nicht nur gut, sondern hervorragend ist – leicht anzupassen, zu erweitern und abzusichern. Genau darum geht es bei DI in Python.
Wenn Sie herausfinden möchten, wie Sie Dependency Injection in Ihre Arbeit integrieren, sind Sie hier genau richtig. In diesem Tutorial erfahren Sie, wie DI funktioniert, wie Sie es einsetzen und wie es Ihre Python-Projekte verbessern kann.
So funktioniert Dependency Injection
Um besser zu verstehen, wie DI funktioniert, stellen Sie sich vor, wie ein Auto zusammengebaut wird. Es wäre wenig sinnvoll, wenn jedes Auto seine eigenen Reifen herstellen müsste, oder? In einer Autofabrik werden die Reifen separat gefertigt und anschließend am Auto montiert. So lassen sie sich austauschen, wenn ein Auto andere Reifen benötigt. DI nutzt dasselbe Prinzip – allerdings in der Softwareentwicklung.
Stellen Sie sich vor, Sie arbeiten an einer Python-Anwendung. Darin gibt es eine Klasse namens DatabaseClient, die bestimmte Angaben benötigt, um eine Verbindung zu einer Datenbank herzustellen. Wenn Sie diese Angaben direkt in die Klasse schreiben, wäre das so, als hätte ein Auto Reifen, die sich niemals austauschen lassen. Bei DI legen Sie diese Angaben jedoch nicht fest in der Klasse ab, sondern stellen sie von außen bereit (oder injizieren sie). Das bedeutet: Wenn Sie später die Angaben ändern oder eine andere Datenbank ausprobieren möchten, können Sie das tun, ohne Ihre gesamte Anwendung auseinanderzunehmen.
Dieser Ansatz bietet zahlreiche Vorteile. Es ist, als hätten Sie einen Werkzeugkasten, aus dem Sie je nach Aufgabe ganz einfach das passende Werkzeug auswählen. Ihre Software wird flexibler: Sie können unkompliziert experimentieren, testen, aktualisieren und Fehler beheben.
Sehen wir uns nun einige Vorteile von Dependency Injection an, um besser zu verstehen, warum Sie sie brauchen.
Warum Sie Dependency Injection brauchen
Bei DI geht es darum, Ihnen mehr Kontrolle zu geben und Ihnen das Programmieren zu erleichtern. Hier sind einige wichtige Gründe, warum Sie DI in Ihren Projekten einsetzen sollten.
Verbessert Wartbarkeit und Testbarkeit des Codes
Dependency Injection ist ein Entwurfsmuster, das die Entkopplung von Komponenten fördert, sodass diese weniger voneinander abhängig sind. Wenn eine bestimmte Komponente oder ein Dienst aktualisiert oder geändert werden muss, können Sie diese Anpassungen vornehmen, ohne dass sie sich auf die gesamte Codebasis auswirken.
Außerdem eignet sich DI hervorragend für Tests. Wenn Komponenten entkoppelt sind, können Sie bei Unit-Tests viel einfacher Mock-Abhängigkeiten injizieren. So können Sie das Verhalten einzelner Teile isoliert testen und dadurch robustere und umfassendere Tests durchführen.
Ermöglicht den einfachen Austausch von Abhängigkeiten
Standen Sie schon einmal vor der großen Herausforderung, Technologien zu wechseln – etwa von einer Datenbanklösung zu einer anderen zu migrieren? Mit DI sind solche Umstellungen deutlich einfacher. Statt die gesamte Klasse oder das Modul, das mit der Datenbank interagiert, umzuschreiben, müssen Sie lediglich die injizierte Abhängigkeit ändern. Diese Flexibilität kann Ihnen viel Zeit sparen und das Fehlerrisiko bei Migrationen oder Aktualisierungen verringern.
Fördert Modularität und Wiederverwendbarkeit des Codes
Dependency Injection fördert das Design von Komponenten, die nicht fest an bestimmte Implementierungen gebunden sind. Dadurch wird Ihre Codebasis modularer. Module oder Komponenten, die mit Blick auf DI entwickelt wurden, lassen sich problemlos in verschiedenen Teilen Ihrer Anwendung oder sogar in ganz anderen Projekten wiederverwenden. Das kann die Entwicklungszeit verkürzen und für einen konsistenteren Programmieransatz sorgen.
Unterstützt die Inversion of Control
Bei der herkömmlichen Programmierung instanziieren und verwalten Klassen häufig ihre eigenen Abhängigkeiten. Bei DI wird dieses Prinzip umgekehrt. Statt der Klasse übernehmen externe Einheiten – oft ein DI-Container oder Framework – die Instanziierung und Bereitstellung dieser Abhängigkeiten. Diese Inversion of Control (IoC) kann die Verwaltung von Abhängigkeiten vereinfachen und eine klarere, besser organisierte Architektur fördern.
Ermöglicht konfigurierbares Anwendungsverhalten
Einer der weniger offensichtlichen, aber wirkungsvollen Vorteile von DI ist, dass sich das Verhalten einer Anwendung spontan ändern lässt. Wenn Sie verschiedene Implementierungen einer Abhängigkeit injizieren, können Sie die Funktionsweise bestimmter Teile Ihrer Anwendung ändern, ohne große Mengen an Code neu zu schreiben. Ob Sie sich an unterschiedliche Laufzeitumgebungen anpassen oder verschiedene Nutzerpräferenzen berücksichtigen möchten: DI gibt Ihnen die Flexibilität, das Verhalten Ihrer Anwendung gezielt anzupassen.
Einschränkungen von Dependency Injection in Python
DI bietet zwar viele Vorteile, bringt aber auch eigene Herausforderungen und Einschränkungen mit sich. Diese sollten Sie berücksichtigen, wenn Sie entscheiden, ob Sie DI in Ihre Python-Projekte integrieren möchten:
Komplexität: DI verändert grundlegend, wie Komponenten oder Objekte in einer Anwendung instanziiert und verwaltet werden. Bei einer umfangreichen Anwendung im großen Maßstab erfordert die Einrichtung von DI eine sorgfältige Zuordnung von Abhängigkeiten, Schnittstellen und den Klassen, die diese implementieren. Diese zusätzliche Ebene kann mehr Boilerplate-Code, mehr Konfiguration und eine steilere Lernkurve bedeuten – insbesondere, wenn Sie ein bestehendes Projekt auf DI umstellen. Damit das gelingt, müssen Sie die Architektur Ihrer Anwendung genau verstehen.
Potenzielle Verwirrung: DI kann abstrakt sein. Wenn Sie oder Ihre Teammitglieder DI noch nicht kennen oder damit wenig vertraut sind, können Ihnen bestimmte Teile der Codebasis rätselhaft erscheinen. Statt Objekte direkt zu instanziieren, werden sie bereitgestellt oder injiziert. Dadurch lässt sich der Ablauf einer Anwendung anfangs schwieriger nachvollziehen. Das kann die Einarbeitung neuer Entwicklerinnen und Entwickler verlängern oder die Lernkurve steiler machen.
Laufzeitfehler: DI-Frameworks verknüpfen Abhängigkeiten oft zur Laufzeit. Bei einer Unstimmigkeit oder fehlenden Konfiguration kann die Anwendung abstürzen oder sich unerwartet verhalten. Solche Fehler sind möglicherweise weder beim Kompilieren noch bei ersten Tests erkennbar und deshalb schwieriger zu diagnostizieren und zu beheben. Sorgfältige Tests und ein gutes Verständnis von DI können dieses Risiko verringern, doch es bleibt eine mögliche Fehlerquelle.
Leistungseinbußen: In vielen Anwendungen ist der zusätzliche Aufwand oft vernachlässigbar. Dennoch kann das Auflösen und Injizieren von Abhängigkeiten zur Laufzeit die Leistung beeinträchtigen. Das ist besonders relevant, wenn jede Millisekunde zählt, etwa bei Hochfrequenz-Handelsplattformen. Messen und profilieren Sie Ihre Anwendung, um diesen Mehraufwand zu verstehen und sie bei Bedarf zu optimieren.
Herausforderungen beim Rollback: Da Abhängigkeiten bei DI eng miteinander verknüpft sind, lässt sich eine Änderung, die Probleme verursacht, nicht immer unkompliziert zurücknehmen. Abhängigkeiten können kaskadierende Auswirkungen haben: Eine Änderung in einem Bereich kann sich auf mehrere andere Komponenten auswirken. Deshalb werden gründliche Tests, Versionskontrolle und sorgfältige Dokumentation noch wichtiger.
Nicht immer notwendig: DI spielt ihre Stärken in komplexen Anwendungen aus, in denen Komponenten lose gekoppelt und isoliert getestet werden müssen und eine bessere Modularität gefragt ist. Für ein einfaches Skript oder eine kleine Anwendung mit nur wenigen Komponenten kann DI jedoch überdimensioniert sein. Das würde die Komplexität erhöhen, ohne greifbare Vorteile zu bieten. Wie bei jedem Entwurfsmuster und jeder Architekturentscheidung sollten Sie prüfen, ob DI für die jeweilige Aufgabe geeignet ist.
Dependency Injection in beliebten Python-Frameworks
In diesem Abschnitt erfahren Sie, wie Sie Dependency Injection mit drei beliebten Python-Frameworks einrichten: Flask, Django und FastAPI. Jedes Framework verfolgt zwar einen eigenen Ansatz, doch allen liegt dasselbe Prinzip zugrunde: Abhängigkeiten zu entkoppeln, um Anwendungen wartbarer, modularer und einfacher testbar zu machen.
Bevor Sie sich mit den Frameworks befassen, sollten Sie sicherstellen, dass Python installiert ist. Falls noch nicht geschehen, können Sie Python von der offiziellen Python-Website herunterladen und installieren.
Nachdem Sie Python auf Ihrem Rechner installiert haben, können Sie sich ansehen, wie Dependency Injection in den verschiedenen Frameworks funktioniert.
Dependency Injection in Flask
Flask ist ein schlankes Web-Framework. Es bietet zwar keine integrierte DI-Unterstützung, aber Sie können Erweiterungen wie Flask-Injector dafür verwenden.
Verwenden Sie den folgenden Befehl, um Flask und Flask-Injector zu installieren:
Flask-Injector integriert die Injector-Bibliothek nahtlos und ermöglicht eine kontrollierte Dependency Injection in Flask-Apps.
Eine Flask-Anwendung erstellen
Erstellen Sie für eine einfache Flask-Anwendung einen Ordner namens flask_di_project. Erstellen Sie dann in diesem Ordner eine neue Datei namens app.py und fügen Sie den folgenden Code hinzu:
In diesem Code erstellen Sie eine Flask-Webanwendung, die Dependency Injection zur Verwaltung ihrer Komponenten nutzt. Die Anwendung besteht aus drei Klassen: Database, Logger und DataService. Sie definieren die Abhängigkeiten der Klasse DataService per Konstruktor-Injektion. So können Sie beim Erstellen einer DataService-Instanz Instanzen von Database und Logger injizieren. Mit dem Decorator @inject für die Index-Route verwenden Sie Flask-Injector, um automatisch eine DataService-Instanz in die Funktion zu injizieren. Außerdem legt eine configure-Funktion fest, wie diese Abhängigkeiten gebunden werden, damit Flask-Injector weiß, wie benötigte Instanzen bereitgestellt werden.
Anwendung ausführen und testen
Wechseln Sie in Ihrem Terminal oder Ihrer Shell in das Verzeichnis, das app.py enthält, und führen Sie Folgendes aus:
Öffnen Sie einen Browser und rufen Sie http://127.0.0.1:5000/ auf. Die folgende Meldung sollte angezeigt werden: {"message": "Data from the Database!"}:

Dependency Injection in Django
Django ist ein High-Level-Web-Framework, das DI über externe Bibliotheken ermöglicht. In diesem Beispiel verwenden Sie die Bibliothek Django Injector, um DI in Ihr Django-Projekt zu integrieren.
Verwenden Sie den folgenden Befehl, um Django und Django Injector zu installieren:
Ein neues Django-Projekt und eine App erstellen
Verwenden Sie die folgenden Befehle, um ein neues Django-Projekt und eine Anwendung zu erstellen:
Nachdem Sie django-injector installiert haben, müssen Sie die Bibliothek zu Ihrem Projekt hinzufügen. Öffnen Sie die Datei settings.py im Ordner django_di_project und fügen Sie django_injector zum Array INSTALLED_APPS hinzu:
Verwenden Sie anschließend in myapp/views.py django-injector, um DI einzurichten:
Hier haben Sie eine Django-View namens MyView definiert. Bei einem GET-Request ruft sie mithilfe des per Dependency Injection bereitgestellten DataService Daten ab und gibt diese als JSON-Antwort zurück.
URLs aktualisieren
Aktualisieren Sie django_di_project/urls.py, damit die Root-URL zu dieser View weiterleitet:
Erstellen Sie anschließend in Ihrem Verzeichnis myapp eine Datei namens services.py und definieren Sie darin die Klasse DataService:
Durch die Einbindung von django-injector haben Sie DI nahtlos in Ihre Django-Anwendung integriert. Der Decorator @inject injiziert die Instanz von DataService effizient in den Konstruktor der Klasse MyView und fördert so die Modularität und bessere Verwaltbarkeit Ihrer Django-Anwendung.
Anwendung ausführen und testen
Führen Sie im Verzeichnis django_di_project in Ihrem Terminal oder Ihrer Shell den folgenden Befehl aus:
Öffnen Sie Ihren Browser und rufen Sie http://127.0.0.1:8000/ auf. Die folgende Meldung sollte angezeigt werden: {"message": "Hello from Dynamic DataService in Django!"}:

Dependency Injection in FastAPI
FastAPI ist ein modernes Web-Framework mit nativer DI-Unterstützung. Wenn Sie FastAPI verwenden, entscheiden Sie sich für ein Framework, in das DI bereits integriert ist. So können Sie nahtlos modulare und testbare Anwendungen erstellen, ohne auf externe Bibliotheken angewiesen zu sein.
Verwenden Sie den folgenden Code, um FastAPI und Uvicorn (einen ASGI-Server zum Ausführen von FastAPI) zu installieren:
Eine einfache FastAPI-Anwendung erstellen
Erstellen Sie zum Anlegen einer einfachen FastAPI-Anwendung in Ihrem Stammverzeichnis einen neuen Ordner namens fast_di_project. Erstellen Sie darin eine Datei namens main.py und fügen Sie den folgenden Code hinzu:
Zunächst erstellen Sie eine Instanz der Klasse FastAPI. Anschließend definieren Sie eine Klasse DataService, die eine Methode zum Abrufen von Daten kapselt. Wenn Sie die Stamm-URL Ihrer Anwendung aufrufen, kommt die integrierte Abhängigkeitsverwaltung von FastAPI zum Einsatz. Sie haben eine Funktion get_data_service() erstellt, die eine Instanz der Klasse DataService bereitstellt. Mit dem Depends-Mechanismus von FastAPI können Sie diese Instanz in die index-Routenfunktion einschleusen. Wenn Sie die Stamm-URL in Ihrem Browser aufrufen, erhalten Sie eine Antwort mit den abgerufenen Daten und einer personalisierten Nachricht des dynamischen DataService in der FastAPI-Welt.
Anwendung ausführen und testen
Wechseln Sie in Ihrem Terminal oder Ihrer Shell in das Verzeichnis mit main.py und führen Sie Folgendes aus:
Öffnen Sie Ihren Browser und rufen Sie http://127.0.0.1:8000/ auf. Die Nachricht {"result":{"message":"Greetings from the Dynamic DataService in the World of FastAPI!"},"info":"Fetched from the Dynamic DataService in FastAPI!"} sollte angezeigt werden:

Der gesamte in diesem Tutorial verwendete Code ist in diesem GitHub-Repository verfügbar.
Python-Frameworks für Dependency Injection
Bei der Auswahl eines DI-Frameworks sollten Sie die konkreten Anforderungen Ihres Projekts, den gewünschten Grad an Modularität und Testbarkeit sowie das verwendete Framework berücksichtigen. Jede Option bietet unterschiedliche Funktionen. Ihre Wahl sollte zur Komplexität und den Designzielen Ihres Projekts passen. Sehen wir uns einige Optionen an:
Flask-Injector
Flask-Injector ist eine ausgezeichnete Wahl, wenn Sie Flask-Anwendungen entwickeln. Besonders wertvoll ist es für schlanke Projekte, die auf dem Flask-Framework basieren.
Flask-Injector fügt sich nahtlos in das Flask-Ökosystem ein und erleichtert die Verwaltung von Abhängigkeiten sowie die Modularisierung Ihrer Flask-App. Für größere und komplexere Anwendungen, die möglicherweise erweiterte Funktionen zur Abhängigkeitsverwaltung erfordern, sollten Sie jedoch auch andere DI-Frameworks in Betracht ziehen, die zusätzliche, auf diese Anforderungen zugeschnittene Möglichkeiten bieten.
Django Injector
Wenn Sie mit Django entwickeln, kann die Bibliothek Django Injector sehr nützlich sein. Sie vereinfacht DI in Django-Projekten und ist damit eine ausgezeichnete Wahl, wenn Sie Modularität und Testbarkeit erreichen möchten.
Verwenden Sie Django Injector für Django-Anwendungen, die eine nahtlose Integration von DI erfordern – insbesondere für klassenbasierte Views, Middleware und Management-Befehle.
Depends
Die Funktion Depends in FastAPI ist für die Zusammenarbeit mit dem Python-Framework asyncio ausgelegt und bietet DI-Funktionen für asynchronen Code. Wenn Sie asynchrone Anwendungen mit FastAPI entwickeln, unterstützt Sie Depends dabei, Abhängigkeiten in asynchrone Funktionen und Coroutinen einzuschleusen und zu verwalten. Das ist besonders wertvoll bei asynchronen Projekten mit FastAPI, wenn Sie Codequalität und ein modulares Design sicherstellen möchten.
Dependency Injector
Die Bibliothek Dependency Injector ist ein umfassendes DI-Framework für verschiedenste Python-Anwendungen. Sie bietet erweiterte Funktionen wie Containerverwaltung, Scoping und Lebenszyklussteuerung.
Dependency Injector eignet sich für größere Projekte, die eine robuste Abhängigkeitsverwaltung, Inversion of Control und komplexe Szenarien erfordern, in denen Sie stärker kontrollieren müssen, wie Abhängigkeiten eingeschleust und verwaltet werden.
Injector
Die Bibliothek Injector ist ein universelles DI-Framework, das sich unabhängig vom Framework in verschiedene Python-Projekte integrieren lässt. Sie eignet sich besonders für Projekte, in denen Sie Dependency Injection manuell konfigurieren und verwalten möchten, ohne auf frameworkspezifische Integrationen zurückzugreifen.
Verwenden Sie Injector, wenn Sie eine flexible und vielseitige DI-Lösung für Anwendungen benötigen, die mehrere Frameworks umfassen oder sogar aus eigenständigen Skripten bestehen.
Sichern Sie die Abhängigkeiten Ihres Projekts mit Snyk
Wenn Sie sich auf die Funktionalität konzentrieren, darf die Sicherheit nicht zu kurz kommen. Hier kann Snyk helfen. Mit dem Snyk-Toolkit können Sie:
Python-Abhängigkeiten scannen, Sicherheitslücken aufspüren und Lösungsvorschläge erhalten.
Beim Programmieren in Echtzeit Feedback zu Sicherheitsproblemen erhalten.
Von automatisierten Lösungen für erkannte Sicherheitslücken profitieren.
Nahtlos in gängige CI/CD-Pipelines (Continuous Integration und Continuous Delivery) integrieren und so regelmäßige Sicherheitsprüfungen sicherstellen.
Indem Sie Snyk-IDE-Erweiterungen in Ihren Workflow integrieren, setzen Sie von Anfang an proaktiv auf sicheres Programmieren und vereinfachen die Verwaltung Ihrer Abhängigkeiten. Weitere Informationen zu Snyk finden Sie in der offiziellen Snyk-Dokumentation.
Fazit
In diesem Artikel haben Sie erfahren, warum Sie Dependency Injection in Python benötigen. Dabei haben Sie einige Vorteile kennengelernt, etwa eine bessere Wartbarkeit und Modularität des Codes. Außerdem haben Sie mögliche Herausforderungen kennengelernt, darunter eine höhere Komplexität und das Risiko von Laufzeitfehlern. Darüber hinaus haben Sie erfahren, wie sich Dependency Injection in weitverbreitete Python-Frameworks wie Flask, FastAPI und Django integrieren lässt.
DI kann zwar gewisse Herausforderungen mit sich bringen, doch wenn Sie ihre Feinheiten beherrschen, können Sie die Qualität Ihrer Python-Anwendungen steigern. Wenn Sie sich mit der Verwaltung von Abhängigkeiten befassen, sind Tools wie Snyk unverzichtbar, um die Sicherheit bei Ihrer Entwicklungsarbeit stets im Blick zu behalten. Weitere Einblicke in die Verwaltung von Python-Abhängigkeiten und Best Practices bietet der Beitrag zum Python-Paketmanager Poetry im Snyk-Blog. Er gibt einen umfassenden Überblick über Tools, mit denen Sie Ihre Python-Projekte effizienter verwalten können.
Starten Sie mit Capture-the-Flag
Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Challenges lösen.
