Python-Abhängigkeitsinjektion: Modularen und testbaren Code erstellen
Lerne, wie du deinen Code modular, testbar und wartbar machst, indem du verschiedene Python-Frameworks für die Abhängigkeitsinjektion verstehst und einsetzt.
Bei der Abhängigkeitsinjektion geht's um ein Designmuster, bei dem Objekte oder Dienste (wir meinen hier Abhängigkeiten) einer Klasse von außen zur Verfügung gestellt werden, anstatt dass die Klasse sie selbst erstellt. Dadurch werden die Komponenten voneinander getrennt.
Die Verwendung von Abhängigkeitsinjektion in Python hat viele Vorteile, zum Beispiel:
Modularität: Dein Code ist in kleinere, wiederverwendbare Teile aufgeteilt.
Testbarkeit: Du kannst deinen Code einfacher testen, weil du die echten Teile durch Scheinteile austauschen kannst.
Wartbarkeit: Du kannst Teile deines Codes aktualisieren oder ändern, ohne den Rest des Systems zu beeinträchtigen.
In diesem Tutorial zeige ich dir anhand einfacher, praktischer Beispiele, wie du Dependency Injection in Python nutzen kannst.
Abhängigkeitsinjektion in Python verstehen
Um zu verstehen, wie man Abhängigkeitsinjektion einsetzt, musst du die wichtigsten Prinzipien dahinter verstehen.
Die Abhängigkeitsinjektion basiert auf dem Prinzip der Umkehrung der Kontrolle (Inversion of Control, IoC). Das heißt, dass eine Klasse ihre Abhängigkeiten nicht selbst erstellt und verwaltet, sondern von einer externen Quelle bekommt. Das hilft dir, dich auf eine Aufgabe zu konzentrieren und deinen Code übersichtlich zu halten.
Das Einfügen von Abhängigkeiten in Python umfasst diese gängigen Methoden, es gibt aber auch noch andere:
Konstruktor-Injektion: Übergib Abhängigkeiten über den Klassenkonstruktor.
Setter-Injektion: Lege Abhängigkeiten mit einer Setter-Methode fest, nachdem das Objekt erstellt wurde.
Methodeninjektion: Gib Abhängigkeiten direkt an die Methode weiter, die sie braucht.
Warum Abhängigkeitsinjektion in Python verwenden?
Als Entwickler kann das Verstehen und Umsetzen von Abhängigkeitsinjektion einen großen Unterschied beim Design deines Codes machen. Schauen wir uns mal ein paar Vorteile der Abhängigkeitsinjektion an.
Modularität, Wiederverwendbarkeit und Flexibilität
Mit der Abhängigkeitsinjektion kannst du deinen Code in kleinere, fokussiertere Teile aufteilen, was dir mehr Flexibilität und Modularität gibt.
Jeder Teil oder jede Komponente erledigt eine bestimmte Aufgabe und hängt von anderen Teilen ab, was die Wiederverwendung in verschiedenen Teilen deiner oder einer anderen Anwendung einfacher macht.
Wenn du zum Beispiel eine Klasse hast, die Nachrichten verschickt. Anstatt eine E-Mail- oder SMS-Logik direkt in deinen Dienst einzubauen, kannst du eine E-Mail- oder SMS-Absenderklasse einfügen.
Jetzt ist es deiner Klasse, die Nachrichten verschickt, egal, wie die Nachricht verschickt wird, sondern sie nutzt einfach den ihr angegebenen Absender. Das heißt, du kannst deinen Absender für Nachrichten mit verschiedenen E-Mail- oder SMS-Absendern verwenden.
Wartbarkeit und Testbarkeit
Mit der Abhängigkeitsinjektion kannst du echte Abhängigkeiten beim Testen ganz einfach durch Scheindaten ersetzen. Das macht es einfacher, Unit-Tests zu schreiben, ohne auf APIs oder Datenbanken angewiesen zu sein.
Stell dir mal vor, du hast eine Klasse, die Daten in einer Datenbank speichert. Zum Testen kannst du statt einer echten Datenbank eine Scheindatenbank verwenden, damit dein Test nicht an die Produktionsdatenbank gekoppelt ist.
Mit der Abhängigkeitsinjektion kannst du bestimmte Teile deines Codes ändern, ohne das ganze System durcheinander zu bringen. Das ist besonders praktisch, wenn du in einer Anwendung den Zahlungsanbieter wechseln musst, zum Beispiel von Stripe zu PayPal, ohne den Rest deines Codes zu ändern.
Kontrollumkehr und austauschbare Abhängigkeiten
Die Idee der Kontrollumkehr ist folgende: Durch die Abhängigkeitsinjektion wird die Verantwortung für die Erstellung und Verwaltung von Abhängigkeiten auf eine externe Quelle verlagert.
Das heißt, du kannst ganz einfach eine Implementierung gegen eine andere austauschen. Wenn du zum Beispiel eine Klasse „ ReportGenerator ” hast, die Berichte formatiert und exportiert, kannst du diese Klasse einfach gegen eine andere austauschen, die dieselbe Funktionalität bietet.
Anstatt dass die Klasse über das Exportformat entscheidet, könntest du eine Exportklasse wie PDFExporter, ExcelExporter oder CSVExporter einfügen.
Die Klasse „ ReportGenerator ” kümmert sich nicht darum, wie der Export passiert. Um also das Format zu ändern, tauscht man einfach den Exporter aus, den man einfügt, ohne „ ReportGenerator ” zu verändern.
Implementierung der Abhängigkeitsinjektion in Python
Jetzt, wo du weißt, was Dependency Injection ist und wozu sie gut ist, schauen wir uns an, wie du sie in Python umsetzen kannst. Fangen wir mit der einfachsten Methode an: der manuellen Abhängigkeitsinjektion.
Manuelle Abhängigkeitsinjektion
Bei der manuellen Abhängigkeitsinjektion erstellst du die Abhängigkeiten selbst und gibst sie an die Klasse oder Funktion weiter, die sie braucht.
Diese Methode eignet sich gut für kleine Anwendungen, aber wenn deine Anwendung wächst, solltest du ein Framework für die Abhängigkeitsinjektion verwenden, um Fehler zu vermeiden.
Hier ist ein Beispiel für Code ohne Abhängigkeitsinjektion
classEmailService:defsend_email(self, message):print(f"Sending email: {message}")classUserNotifier:def__init__(self):
self.email_service = EmailService()# Creates its own dependencydefnotify(self, message):
self.email_service.send_email(message)
notifier = UserNotifier()
notifier.notify("Welcome!")Bereitgestellt von
Im obigen Beispiel ist „ UserNotifier “ eng mit „ EmailService “ verbunden. EmailService s kann zum Testen nicht einfach ausgetauscht oder simuliert werden.
Hier ist noch eine andere Version, aber mit manueller Abhängigkeitsinjektion.
Container legt fest, wie Instanzen erstellt werden
Singleton sorgt dafür, dass nur eine Instanz von „ EmailService ” existiert
Factory Erstellt eine neue „ UserNotifier “, in die automatisch „ EmailService “ eingefügt wird.
Verdrahtungsmechanismus
Du kannst Dekoratoren wie „ @inject “ verwenden, um manuelle Abhängigkeitsübergaben zu reduzieren.
Hier ist noch eine andere Version vom vorherigen Beispiel:
from dependency_injector import containers, providers
# Define servicesclassEmailService:defsend_email(self, message):print(f"Sending email: {message}")classUserNotifier:def__init__(self, email_service: EmailService):
self.email_service = email_service
defnotify(self, message):
self.email_service.send_email(message)# Create containerclassContainer(containers.DeclarativeContainer):
email_service = providers.Singleton(EmailService)
user_notifier = providers.Factory(UserNotifier, email_service=email_service)from dependency_injector.wiring import inject, Provide
@injectdefmain(notifier: UserNotifier = Provide[Container.user_notifier]):
notifier.notify("Hello!")if __name__ =="__main__":
container = Container()
container.wire(modules=[__name__])# This wires the current module
main()Bereitgestellt von
In der aktuellen Version;
Ohne die Abhängigkeit manuell hinzuzufügen, fügt der Dekorator „ @inject “ die Funktions- oder Klassenabhängigkeit automatisch ein.
Die Klasse „ Provide ” sagt dem „ dependency-injector ”, welche Abhängigkeit eingefügt werden soll.
Mit diesem Ansatz musst du keine manuellen Verdrahtungsabhängigkeiten vom Container mehr machen.
Das ist praktisch bei größeren Anwendungen, wo Dienste von mehreren Komponenten abhängen können (deshalb müssen sie automatisch gestartet werden).
Verwendung von Dependency Injection in gängigen Python-Frameworks
Die meisten modernen Python-Frameworks machen es einfach, mit Abhängigkeitsinjektion zu arbeiten, sodass du saubereren und besser testbaren Code schreiben kannst.
In diesem Abschnitt erfährst du, wie die Abhängigkeitsinjektion in Flask, Django und FastAPI angewendet wird.
Abhängigkeitsinjektion in Flask
Flask hat keine eingebaute Unterstützung für die Abhängigkeitsinjektion, aber du kannst sie mit der Bibliothek „ dependency-injector ” in den folgenden Schritten implementieren.
Schritt 1: Deine Services festlegen
classGreetingService:defget_greeting(self, name):returnf"Hello, {name}!"Bereitgestellt von
Schritt 2: Richte deinen Dependency-Injection-Container ein
from dependency_injector import containers, providers
classContainer(containers.DeclarativeContainer):
greeting_service = providers.Factory(GreetingService)Bereitgestellt von
Schritt 3: Erstell die Flask-App und füge Abhängigkeiten hinzu.
from flask import Flask, request
from dependency_injector.wiring import inject, Provide
container = Container()
app = Flask(__name__)@app.route("/greet")@injectdefgreet(greeting_service: GreetingService = Provide[Container.greeting_service]):
name = request.args.get("name","World")return greeting_service.get_greeting(name)
container.wire(modules=[__name__])if __name__ =="__main__":
app.run(debug=True)Bereitgestellt von
Aus dem obigen Code:
Die Funktion „ name = request.args.get(“name”, “World”) “ holt „ name “ aus der Abfragezeichenfolge raus und setzt standardmäßig „ ”World” “, wenn nichts angegeben ist.
return greeting_service.get_greeting(name) ruft die Methode des eingebundenen Dienstes auf und gibt eine Begrüßungsmeldung zurück.
container.wire(modules=[__name__]) sagt dem Dependency Injector, dass er dieses Modul nach Dekoratoren vom Typ „ @inject “ und Markern vom Typ „ Provide[...] “ durchsuchen soll, um sie mit den tatsächlichen Anbietern zu verbinden.
Schritt 4: Probier die App aus
Starte die Anwendung auf deinem Terminal mit dem Befehl „ python .py “ und geh auf die URL „ http://localhost:5000/greet?name=Jacob “.
Du solltest jetzt Folgendes sehen.
Abhängigkeitsinjektion in Django
Genau wie Flask hat Django keine eingebaute Abhängigkeitsinjektion. Du kannst aber die klassenbasierten Ansichten, Middleware und App-Struktur nutzen, um die Abhängigkeitsinjektion manuell oder mit der Bibliothek „ dependency-injector “ einzubauen.
Hier ist eine Schritt-für-Schritt-Anleitung, wie du Dependency Injection in Django einbaust.
Schritt 1: Deinen Service festlegen
Erstell einen einfachen Dienst mit deiner Geschäftslogik in einer neuen Datei „ services.py ” in deinem App-Ordner.
# myapp/services.pyclassGreetingService:defget_greeting(self, name):returnf"Hello, {name}!"Bereitgestellt von
Schritt 2: Richte den Dependency-Injector-Container ein
Erstell in deinem Anwendungsverzeichnis einen Container mit der Bibliothek „ dependency-injector ” in einer neuen Datei „ containers.py ”.
Schritt 3: Erstell eine Ansicht mit eingebundenen Abhängigkeiten
Verwende den Dekorator „ @inject ” in deiner Ansicht, um Abhängigkeiten in „ views.py ” zu erhalten.
# myapp/views.pyfrom django.http import HttpResponse
from dependency_injector.wiring import inject, Provide
from.containers import Container
from.services import GreetingService
@injectdefgreet_view(
request, greeting_service: GreetingService = Provide[Container.greeting_service]):
name = request.GET.get("name","World")
message = greeting_service.get_greeting(name)return HttpResponse(message)Bereitgestellt von
Schritt 4: Aktualisiere die Datei „urls.py“
Geh in deinem Projektordner zur Datei „ urls.py “ und hänge deinen Abhängigkeitsinjektor in das URL-Routing-System von Django ein.
from django.contrib import admin
from django.urls import path
from myapp.views import greet_view
urlpatterns =[
path("admin/", admin.site.urls),
path("greet/", greet_view),]Bereitgestellt von
Schritt 5: Verbinde den Container in apps.py
Geh auf „ apps.py “ und verbinde den Container mit der App.
# myapp/apps.pyfrom django.apps import AppConfig
from.containers import Container
classMyAppConfig(AppConfig):
default_auto_field ="django.db.models.BigAutoField"
name ="myapp"defready(self):
container = Container()
container.wire(modules=["myapp.views"])Bereitgestellt von
Geh dann zu deiner App-Konfiguration unter „ INSTALLED_APPS “ und füge deine Anwendung hinzu.
# settings.py
INSTALLED_APPS =['myapp.apps.MyAppConfig',...]Bereitgestellt von
Schritt 6: Die Ansicht testen
Starte deinen Django-Server und ruf die Ansicht über die URL http://localhost:8000/greet/?name=Django auf.
Du solltest jetzt Folgendes sehen.
Abhängigkeitsinjektion in FastAPI
FastAPI hat eine eingebaute Unterstützung für die Abhängigkeitsinjektion über den speziellen Marker „ Depends() “, der alles injiziert, was eine Funktion zurückgibt.
Hier ist eine Schritt-für-Schritt-Anleitung, wie du die Abhängigkeitsinjektion in FastAPI machst.
Schritt 1: Einen Service definieren
classGreetingService:defget_greeting(self, name:str)->str:returnf"Hello, {name}!"Bereitgestellt von
Schritt 3: Richte die FastAPI-App ein und nutze Dependency Injection.
app = FastAPI()@app.get("/greet")defgreet(
name:str="World", service: GreetingService = Depends(get_greeting_service)):return{"message": service.get_greeting(name)}Bereitgestellt von
Schritt 4: Starte die App und probier sie aus
Starte deinen Server mit dem Befehl „ uvicorn main:app –reload “ und ruf die URL „ http://localhost:8000/greet?name=FastAPI “ auf. Du solltest die folgende Antwort sehen.
Vergleich von Python-Frameworks für die Abhängigkeitsinjektion
Es gibt verschiedene Python-Abhängigkeits-Frameworks, die alle ihre eigenen Vorteile, Syntax und Anwendungsfälle haben. Welches Framework du nimmst, hängt von der Größe und Struktur deines Projekts ab.
Hier ist ein Vergleich einiger gängiger Python-Frameworks für die Abhängigkeitsinjektion.
Bietet Abhängigkeitsinjektion für Ansichten und Middleware.
Hängt davon ab injector
Fortgeschrittene Implementierungsmuster
Wenn dein Anwendungscode immer größer wird, wird es echt schwierig, die Abhängigkeitsinjektion zu verwalten.
Mit der erweiterten Abhängigkeitsinjektion kannst du Aufgaben wie Ressourcen im Anforderungsbereich, Bereinigung und Trennung von Anliegen erledigen.
Abhängigkeiten mit Gültigkeitsbereich
Mit bereichsbezogenen Abhängigkeiten kannst du steuern, wie lange eine Abhängigkeit bestehen bleibt, z. B. wenn du ein neues Objekt pro Anfrage, ein gemeinsames Objekt für die gesamte Lebensdauer der App oder die automatische Bereinigung von Ressourcen wie Datenbankverbindungen möchtest.
Die meisten Dependency-Injector-Frameworks unterstützen Bereiche wie:
Singleton: Ein Instanz für die ganze App.
Fabrik: Jedes Mal, wenn es eingefügt wird, gibt's eine neue Instanz.
Thread-lokal oder Anfrage: Nur eine Instanz pro Thread/Anfrage.
Quelle: Verwaltet mit einer Logik zum Einrichten und Abbauen.
Hier ist ein Beispiel für bereichsbezogene Abhängigkeiten mit verschachtelten Containern, die „ dependency-injector “ verwenden.
from dependency_injector import containers, providers, resources
# A simple resource that needs setup and cleanupclassMyResource:def__enter__(self):print("Setting up the resource")return self
def__exit__(self, exc_type, exc_val, exc_tb):print("Cleaning up the resource")# Create a container to manage the resourceclassMyContainer(containers.DeclarativeContainer):
resource = providers.Resource(MyResource)# Using the resource with a context manager
container = MyContainer()with container.resource()as r:print("Using the resource")Bereitgestellt von
Im obigen Beispiel macht „ MyResource “ eine Klasse nach, die Setup und Teardown braucht, wie zum Beispiel das Öffnen/Schließen einer Datei oder Datenbank.
Die „ container ” stellt die Ressource über „ providers.Resource ” bereit, und wenn du „ with container.resource() ” verwendest, wird der Kontext korrekt aufgerufen und verlassen.
Dieses Muster sorgt dafür, dass Ressourcen wie Datei-Handles, Datenbankverbindungen oder Netzwerksitzungen sauber verwaltet werden.
Stell dir einen API-Endpunkt vor, der für die aktuelle Anfrage eine Datenbanksitzung oder einen Logger mit anfragespezifischem Kontext braucht.
Bei bereichsbezogenen Abhängigkeiten erstellt das Framework einen Container für den Anforderungsbereich, wenn die Anforderung beginnt, alle Abhängigkeiten aus diesem Bereich aufzulösen.
Wenn die Anfrage beendet ist, werden alle notwendigen Aufräumarbeiten, wie das Schließen von Datenbanksitzungen, automatisch erledigt. Dadurch wird sichergestellt, dass Zustände nicht über Anfragen hinweg geteilt werden und keine Ressourcenlecks entstehen.
Asynchrone Injektion
Um Aufgaben, die viel mit Ein- und Ausgabe zu tun haben, effizient zu erledigen, brauchst du eine asynchrone Ausführung. Abhängigkeitsinjektionssysteme in Frameworks wie FastAPI und dependency-injector bieten asynchrone Injektion über async/await.
async defasync resources Asynchrone Injektion bedeutet, Abhängigkeiten, die asynchrone Funktionen sind, einzufügen oder asynchrone Datenbankverbindungen, externe APIs oder Hintergrundaufgaben zu verwalten.
Anstatt sie synchron aufzulösen, wartet das Abhängigkeitsinjektionssystem, bis sie aufgelöst sind, bevor es die Injektion durchführt.
Hier ist ein Beispiel für eine asynchrone Abhängigkeit in FastAPI:
from fastapi import FastAPI, Depends
app = FastAPI()# Async dependencyasyncdefget_user():# Simulate async I/Oawait some_async_db_query()return{"name":"Philip Jones"}@app.get("/profile")asyncdefread_profile(user:dict= Depends(get_user)):return{"user": user}Bereitgestellt von
FastAPI kümmert sich automatisch um die asynchrone Auflösung von „ get_user “ und fügt das Ergebnis ein.
dependency-injector unterstützt auch die asynchrone Lebenszyklusverwaltung mithilfe von Anwendungsdienstanbietern ( AsyncResource ).
from dependency_injector import containers, providers, resources
classAsyncDB:asyncdefconnect(self):print("Connected")return self
asyncdefdisconnect(self):print("Disconnected")classAsyncDBResource(resources.AsyncResource):asyncdefinit(self)-> AsyncDB:
db = AsyncDB()returnawait db.connect()asyncdefshutdown(self, db: AsyncDB):await db.disconnect()classContainer(containers.DeclarativeContainer):
db = providers.Resource(AsyncDBResource)# Usageasyncdefmain():
container = Container()await container.init_resources()
db =await container.db()# Get the AsyncDB instance# Use db here...await container.shutdown_resources()Bereitgestellt von
Im obigen Code:
resources.AsyncResource wird für die asynchrone Einrichtungs-/Abbau-Logik verwendet.
await container.init_resources() initialisiert alle Ressourcen und ruft „ init “ auf.
await container.shutdown_resources() räumt alle Ressourcen auf.
await container.db() holt die eigentliche Ressourceninstanz, also AsyncDB.
Mit der asynchronen Abhängigkeitsinjektion bleibt deine App leistungsstark, weil synchrone I/O in asynchronen Routen vermieden werden und du nicht mehr manuell auf Abhängigkeiten in deiner Logik warten musst.
Sicherheitsauswirkungen
Abhängigkeitsinjektion macht zwar alles modularer und testbarer, kann aber auch Sicherheitsrisiken mit sich bringen, wenn man nicht aufpasst.
Ein großes Risiko ist die Verwirrung bei Abhängigkeiten, wenn bösartige Pakete legitime Pakete imitieren. Zum Beispiel haben Angreifer Pakete auf PyPI, die Namen aus internen Unternehmenssystemen haben, um Apps dazu zu bringen, die bösartige Version zu nutzen.
Um auf der sicheren Seite zu bleiben, solltest du darauf achten, dass du niemals nicht vertrauenswürdige oder vom Benutzer kontrollierte Komponenten einfügst und Konfigurationsquellen wie Umgebungsvariablen vor dem Einfügen überprüfst.
Verwende die Dateien „ requirements.txt “ oder „ poetry.lock “, um bestimmte Versionen zu sperren und Sicherheitsscans bei Änderungen an Abhängigkeiten automatisch durchzuführen. Außerdem solltest du deine Abhängigkeiten regelmäßig auf Schwachstellen überprüfen.
Die zentrale Verwaltung deiner Abhängigkeiten ist auch wichtig, weil sie dafür sorgt, dass alles in deinen Anwendungen gleich läuft und Sicherheits-Patches oder Versions-Updates einfacher installiert werden können. Das hilft dabei, „Schattenabhängigkeiten” zu vermeiden, die auf verschiedene Arten in den Code eingeschleust werden können.
Es gibt ein paar Tools, die Risiken erkennen und deine Sicherheit verbessern können:
Snyk: Sucht nach bekannten Schwachstellen in Python-Paketen und schlägt Lösungen vor.
Sicherheit: Überprüft auf unsichere Pakete und Schwachstellen im Code.
Bandit: Ein statischer Code-Analysator für Python, der sich auf Sicherheitsprobleme konzentriert.
Praktische Anwendungen
Die Abhängigkeitsinjektion ist nicht nur so ein theoretisches Muster, sondern wird in vielen Produktionssystemen eingesetzt, um die Komplexität zu verwalten, die Flexibilität zu verbessern und das Testen zu optimieren.
Webdienst-Einstellungen
In modernen Web-Apps ist die Abhängigkeitsinjektion echt wichtig für die Verwaltung von Standarddiensten wie Authentifizierung, Protokollierung und Datenbankzugriff.
Frameworks wie FastAPI sind echt wichtig, um Routen und Abhängigkeiten zu klären.
Das folgende Beispiel zeigt, wie man eine zentralisierte Authentifizierung in FastAPI macht.
from fastapi import Depends, HTTPException
defget_current_user(token:str= Depends(oauth2_scheme)):
user = verify_token(token)ifnot user:raise HTTPException(status_code=401)return user
@app.get("/protected")defprotected_route(user:dict= Depends(get_current_user)):return{"message":f"Hello {user['name']}"}Bereitgestellt von
Im obigen Code wird „ get_current_user() ” als Routenabhängigkeit eingefügt, aber die Logik zum Überprüfen des Tokens ist zentralisiert und kann wiederverwendet werden.
Du kannst das auch für den Datenbankzugriff machen, sodass die Datenbanksitzungen automatisch verwaltet und bereinigt werden.
Hier ist ein Beispiel, wie du mit Abhängigkeitsinjektion den Datenbankzugriff zentralisieren kannst.
from sqlalchemy.ext.asyncio import AsyncSession
asyncdefget_db_session()-> AsyncSession:asyncwith async_session()as session:yield session
@app.get("/items")asyncdefread_items(db: AsyncSession = Depends(get_db_session)):returnawait db.execute("SELECT * FROM items")Bereitgestellt von
Im obigen Code ist „ get_db_session ” ein Abhängigkeitsanbieter. Wenn die Route „ read_items “ aufgerufen wird, fügt FastAPI automatisch den Parameter „ db “ ein, sodass die Route auf die Datenbank zugreifen kann, ohne dass die Sitzung manuell eingerichtet oder beendet werden muss.
Entwicklung mit Tests
Mit der Abhängigkeitsinjektion kannst du ganz einfach die Abhängigkeiten ändern, die Komponenten bekommen, was beim Testen echt wichtig ist.
Du kannst echte Dienste während des Testens durch Mocks ersetzen,wodurch dein Code modularer und einfacher zu testen ist.
FastAPI und „ dependency-injector ” können Abhängigkeiten beim Testen überschreiben.
Hier ist ein Beispiel in FastAPI.
from fastapi.testclient import TestClient
from main import app, get_db_session
defoverride_db():return TestDBSession()
app.dependency_overrides[get_db_session]= override_db
client = TestClient(app)
response = client.get("/items")assert response.status_code ==200Bereitgestellt von
Im obigen Code-Beispiel wird „ get_db_session “ während der Tests mit einer Scheindatenbank überschrieben, sodass der Produktionscode nicht gepatcht oder geändert werden muss.
Das Gleiche gilt auch für „ depedency-injector “. Du kannst deine echte Datenbank für Testzwecke mit einer Scheindatenbank überschreiben, wie im folgenden Beispiel gezeigt.
from containers import Container
deftest_service_behavior():
container = Container()
container.db.override(providers.Factory(TestDB))
service = container.service()assert service.get_data()=="mocked"Bereitgestellt von
Die Methode „ override() “ ersetzt die echte Abhängigkeit durch eine Scheinabhängigkeit, die sauber, kontrollierbar und reversibel ist.
Hier sind ein paar Tipps, die du bei der Überschreibung von Abhängigkeiten beachten solltest:
Konstruktor-Injektion: Stell sicher, dass du Mocks direkt an den Konstruktor übergibst.
Framework-Überschreibungen: Verwende die integrierten Überschreibungswerkzeuge wie „ FastAPI.dependency_overrides “ in FastAPI oder „ dependency_injector.override() “ in „ dependency-injector “.
Spielplan: Nutze Testframeworks wie pytest, um wiederverwendbare Mocks zu erstellen und sie über Fixtures einzufügen.
Bewährte Vorgehensweisen und Anti-Muster
Obwohl die Abhängigkeitsinjektion den Code modular und leicht testbar machen soll, kann sie zu Fehlern führen oder den Code kompliziert machen.
Empfohlene Vorgehensweisen
Hier sind ein paar Tipps, die du beim Erstellen von Abhängigkeiten beachten solltest:
Programmiere Schnittstellen, nicht Implementierungen: Definiere deine Abhängigkeiten mit abstrakten Basisklassen oder Schnittstellen statt mit komplizierten Implementierungen. Das macht deinen Code einfacher zu testen und fördert auch die Verwendung von Mocks in Unit-Tests.
Bevorzugt Komposition gegenüber Vererbung: Anstatt komplizierte Klassenhierarchien aufzubauen, solltest du Objekte mit injizierten Abhängigkeiten zusammenstellen, damit die Komponenten klein und fokussiert bleiben.
Zentrale Abhängigkeitskonfiguration: Verwende einen einzigen Container oder ein einziges Modul, um Abhängigkeiten zu definieren und zu verwalten, was das Überschreiben und Testen vereinfacht.
Vermeide zirkuläre Abhängigkeiten: Entwerfen Sie Ihren Abhängigkeitsgraphen sorgfältig, um zirkuläre Referenzen zu vermeiden, indem Sie Factory-Funktionen oder Provider verwenden, um die Instanziierung zu verzögern.
Häufige Fehler
Hier sind ein paar häufige Fehler, die du bei der Verwendung von Abhängigkeitsinjektion vermeiden solltest.
Anti-Muster für den Service-Locator: Vermeide globale Container, um Abhängigkeiten dynamisch abzurufen, da das Abhängigkeiten verstecken und den Code schwer verständlich und testbar machen kann.
Überdosierung: Vermeide es, zu viele Dienste in eine einzige Klasse oder Funktion zu packen, damit dein Code nicht zu aufgeblähten Konstruktoren und schwer zu testender Logik führt.
Fehlendes Scope-Management und staatliche Verluste: Stell sicher, dass du immer den richtigen Bereich verwendest, um zu vermeiden, dass Zustände zwischen Benutzern oder Tests geteilt werden.
Testen von Abhängigkeitsinjektionen in Python
Mit der Abhängigkeitsinjektion kannst du ganz einfach Mock-Objekte in deine Komponenten einfügen, was Unit-Tests einfacher macht. So kannst du grundlegende Teile von testspezifischen Teilen trennen.
Mit der Abhängigkeitsinjektion sind Tests deterministisch, haben keine Nebeneffekte und führen zu einer schnelleren Testausführung.
Hier ist ein Beispiel, das zeigt, wie du mit Abhängigkeitsinjektion Mock-, Stub- oder Fake-Versionen dieser Abhängigkeiten in deine Tests einfügen kannst.
classTestEmailService:defsend_email(self, to, message):
self.sent_to = to
deftest_notifier_sends_email():
test_email = TestEmailService()
notifier = Notifier(test_email)
notifier.alert("[email protected]")assert test_email.sent_to =="[email protected]"Bereitgestellt von
Achte darauf, dass du oft Abhängigkeitsinjektion verwendest und Monkey Patching vermeidest, um die Wartbarkeit zu verbessern und zu verhindern, dass es bei internen Namensänderungen zu Problemen kommt.
Fazit
Auch wenn du für kleine Projekte vielleicht nicht gleich eine Abhängigkeitsinjektion brauchst, kannst du mit zunehmender Größe und Skalierung des Projekts von der Struktur und Modularität profitieren, die die Abhängigkeitsinjektion bietet.
Die Zukunft der Abhängigkeitsinjektion in Python sieht vielversprechend aus und wir können davon ausgehen, dass sie mit einem verbesserten Typsystem und einer optimierten Verwaltung des asynchronen Lebenszyklus weiterentwickelt wird.
Das Beherrschen der Abhängigkeitsinjektion ist wichtig, um ein guter Python-Entwickler zu werden. Aber das ist noch nicht alles: Viele andere fortgeschrittene Konzepte können deine Python-Kenntnisse noch weiter verbessern.
Um dir beim Lernen zu helfen, findest du hier ein paar Ressourcen, die du dir mal anschauen solltest.
Erfahrener Data-Science-Dozent und Biostatistiker mit Kenntnissen in Python, R und maschinellem Lernen.
Häufig gestellte Fragen
Was ist Abhängigkeitsinjektion?⌄
Abhängigkeitsinjektion ist ein Entwurfsmuster, mit dem du saubereren Code schreiben kannst, indem du Abhängigkeiten innerhalb einer Klasse erstellst, die von außen übergeben werden können, wodurch dein Code einfacher zu verwalten und zu ändern ist.
Was sind die Hauptvorteile der Abhängigkeitsinjektion?⌄
Abhängigkeitsinjektion macht deinen Code modular, testbar und wartbar.
Wie kann ich Abhängigkeitsinjektion in Python machen?⌄
Du kannst die Abhängigkeitsinjektion in Python entweder manuell oder mithilfe von Bibliotheken wie dependency_injector implementieren.
Was ist asynchrone Injektion?⌄
async defasync resources Asynchrone Injektion bedeutet, Abhängigkeiten, die asynchrone Funktionen sind, einzufügen oder asynchrone Datenbankverbindungen, externe APIs oder Hintergrundaufgaben zu verwalten.
Was sind die Sicherheitsrisiken von Dependency Injection und wie kann ich damit umgehen?⌄
Ein großes Risiko ist die Verwirrung bei Abhängigkeiten, wenn bösartige Pakete legitime Pakete imitieren. Um das zu vermeiden, kannst du Tools wie Snyk, Xygeni, Safety oder Bandit nutzen, um deinen Code und deine Pakete auf Schwachstellen zu checken.
Lerne mehr über abstrakte Klassen in Python, wozu sie gut sind und wie du mit dem Modul „abc“ einheitliche Schnittstellen sicherstellen kannst. Enthält praktische Beispiele und bewährte Methoden für eine effektive Umsetzung.
Lerne mehr über Python-Lambda-Funktionen, wozu sie gut sind und wann man sie benutzt. Enthält praktische Beispiele und bewährte Methoden für eine effektive Umsetzung.
Bei der Abhängigkeitsinjektion geht's um ein Designmuster, bei dem Objekte oder Dienste (wir meinen hier Abhängigkeiten) einer Klasse von außen zur Verfügung gestellt werden, anstatt dass die Klasse sie selbst erstellt. Dadurch werden die Komponenten voneinander getrennt.
Die Verwendung von Abhängigkeitsinjektion in Python hat viele Vorteile, zum Beispiel:
In diesem Tutorial zeige ich dir anhand einfacher, praktischer Beispiele, wie du Dependency Injection in Python nutzen kannst.
Abhängigkeitsinjektion in Python verstehen
Um zu verstehen, wie man Abhängigkeitsinjektion einsetzt, musst du die wichtigsten Prinzipien dahinter verstehen.
Die Abhängigkeitsinjektion basiert auf dem Prinzip der Umkehrung der Kontrolle (Inversion of Control, IoC). Das heißt, dass eine Klasse ihre Abhängigkeiten nicht selbst erstellt und verwaltet, sondern von einer externen Quelle bekommt. Das hilft dir, dich auf eine Aufgabe zu konzentrieren und deinen Code übersichtlich zu halten.
Das Einfügen von Abhängigkeiten in Python umfasst diese gängigen Methoden, es gibt aber auch noch andere:
Warum Abhängigkeitsinjektion in Python verwenden?
Als Entwickler kann das Verstehen und Umsetzen von Abhängigkeitsinjektion einen großen Unterschied beim Design deines Codes machen. Schauen wir uns mal ein paar Vorteile der Abhängigkeitsinjektion an.
Modularität, Wiederverwendbarkeit und Flexibilität
Mit der Abhängigkeitsinjektion kannst du deinen Code in kleinere, fokussiertere Teile aufteilen, was dir mehr Flexibilität und Modularität gibt.
Jeder Teil oder jede Komponente erledigt eine bestimmte Aufgabe und hängt von anderen Teilen ab, was die Wiederverwendung in verschiedenen Teilen deiner oder einer anderen Anwendung einfacher macht.
Wenn du zum Beispiel eine Klasse hast, die Nachrichten verschickt. Anstatt eine E-Mail- oder SMS-Logik direkt in deinen Dienst einzubauen, kannst du eine E-Mail- oder SMS-Absenderklasse einfügen.
Jetzt ist es deiner Klasse, die Nachrichten verschickt, egal, wie die Nachricht verschickt wird, sondern sie nutzt einfach den ihr angegebenen Absender. Das heißt, du kannst deinen Absender für Nachrichten mit verschiedenen E-Mail- oder SMS-Absendern verwenden.
Wartbarkeit und Testbarkeit
Mit der Abhängigkeitsinjektion kannst du echte Abhängigkeiten beim Testen ganz einfach durch Scheindaten ersetzen. Das macht es einfacher, Unit-Tests zu schreiben, ohne auf APIs oder Datenbanken angewiesen zu sein.
Stell dir mal vor, du hast eine Klasse, die Daten in einer Datenbank speichert. Zum Testen kannst du statt einer echten Datenbank eine Scheindatenbank verwenden, damit dein Test nicht an die Produktionsdatenbank gekoppelt ist.
Mit der Abhängigkeitsinjektion kannst du bestimmte Teile deines Codes ändern, ohne das ganze System durcheinander zu bringen. Das ist besonders praktisch, wenn du in einer Anwendung den Zahlungsanbieter wechseln musst, zum Beispiel von Stripe zu PayPal, ohne den Rest deines Codes zu ändern.
Kontrollumkehr und austauschbare Abhängigkeiten
Die Idee der Kontrollumkehr ist folgende: Durch die Abhängigkeitsinjektion wird die Verantwortung für die Erstellung und Verwaltung von Abhängigkeiten auf eine externe Quelle verlagert.
Das heißt, du kannst ganz einfach eine Implementierung gegen eine andere austauschen. Wenn du zum Beispiel eine Klasse „
ReportGenerator” hast, die Berichte formatiert und exportiert, kannst du diese Klasse einfach gegen eine andere austauschen, die dieselbe Funktionalität bietet.Anstatt dass die Klasse über das Exportformat entscheidet, könntest du eine Exportklasse wie
PDFExporter,ExcelExporteroderCSVExportereinfügen.Die Klasse „
ReportGenerator” kümmert sich nicht darum, wie der Export passiert. Um also das Format zu ändern, tauscht man einfach den Exporter aus, den man einfügt, ohne „ReportGenerator” zu verändern.Implementierung der Abhängigkeitsinjektion in Python
Jetzt, wo du weißt, was Dependency Injection ist und wozu sie gut ist, schauen wir uns an, wie du sie in Python umsetzen kannst. Fangen wir mit der einfachsten Methode an: der manuellen Abhängigkeitsinjektion.
Manuelle Abhängigkeitsinjektion
Bei der manuellen Abhängigkeitsinjektion erstellst du die Abhängigkeiten selbst und gibst sie an die Klasse oder Funktion weiter, die sie braucht.
Diese Methode eignet sich gut für kleine Anwendungen, aber wenn deine Anwendung wächst, solltest du ein Framework für die Abhängigkeitsinjektion verwenden, um Fehler zu vermeiden.
Hier ist ein Beispiel für Code ohne Abhängigkeitsinjektion
Im obigen Beispiel ist „
UserNotifier“ eng mit „EmailService“ verbunden.EmailServices kann zum Testen nicht einfach ausgetauscht oder simuliert werden.Hier ist noch eine andere Version, aber mit manueller Abhängigkeitsinjektion.
Die obige Version ist flexibel und lässt verschiedene Messaging-Dienste einbinden. Außerdem kannst du
EmailServicein Tests durch einen Mock ersetzen.Verwendung des Dependency-Injector-Frameworks
Wenn deine Anwendung wächst, wird es kompliziert und chaotisch, Abhängigkeiten zu verwalten.
Der Rahmen bietet einen strukturierten Ansatz.
dependency-injectorRahmen bietet einen strukturierten Ansatz.Container-Anbieter-Architektur
Das Framework „
dependency-injector“ funktioniert mit einer Container-Provider-Architektur.Im obigen Beispiel:
Containerlegt fest, wie Instanzen erstellt werdenSingletonsorgt dafür, dass nur eine Instanz von „EmailService” existiertFactoryErstellt eine neue „UserNotifier“, in die automatisch „EmailService“ eingefügt wird.Verdrahtungsmechanismus
Du kannst Dekoratoren wie „
@inject“ verwenden, um manuelle Abhängigkeitsübergaben zu reduzieren.Hier ist noch eine andere Version vom vorherigen Beispiel:
In der aktuellen Version;
Ohne die Abhängigkeit manuell hinzuzufügen, fügt der Dekorator „
@inject“ die Funktions- oder Klassenabhängigkeit automatisch ein.Die Klasse „
Provide” sagt dem „dependency-injector”, welche Abhängigkeit eingefügt werden soll.Mit diesem Ansatz musst du keine manuellen Verdrahtungsabhängigkeiten vom Container mehr machen.
Das ist praktisch bei größeren Anwendungen, wo Dienste von mehreren Komponenten abhängen können (deshalb müssen sie automatisch gestartet werden).
Verwendung von Dependency Injection in gängigen Python-Frameworks
Die meisten modernen Python-Frameworks machen es einfach, mit Abhängigkeitsinjektion zu arbeiten, sodass du saubereren und besser testbaren Code schreiben kannst.
In diesem Abschnitt erfährst du, wie die Abhängigkeitsinjektion in Flask, Django und FastAPI angewendet wird.
Abhängigkeitsinjektion in Flask
Flask hat keine eingebaute Unterstützung für die Abhängigkeitsinjektion, aber du kannst sie mit der Bibliothek „
dependency-injector” in den folgenden Schritten implementieren.Schritt 1: Deine Services festlegen
Schritt 2: Richte deinen Dependency-Injection-Container ein
Schritt 3: Erstell die Flask-App und füge Abhängigkeiten hinzu.
Aus dem obigen Code:
Die Funktion „
name = request.args.get(“name”, “World”)“ holt „name“ aus der Abfragezeichenfolge raus und setzt standardmäßig „”World”“, wenn nichts angegeben ist.return greeting_service.get_greeting(name)ruft die Methode des eingebundenen Dienstes auf und gibt eine Begrüßungsmeldung zurück.container.wire(modules=[__name__])sagt dem Dependency Injector, dass er dieses Modul nach Dekoratoren vom Typ „@inject“ und Markern vom Typ „Provide[...]“ durchsuchen soll, um sie mit den tatsächlichen Anbietern zu verbinden.Schritt 4: Probier die App aus
Starte die Anwendung auf deinem Terminal mit dem Befehl „
python .py“ und geh auf die URL „http://localhost:5000/greet?name=Jacob“.Du solltest jetzt Folgendes sehen.
Abhängigkeitsinjektion in Django
Genau wie Flask hat Django keine eingebaute Abhängigkeitsinjektion. Du kannst aber die klassenbasierten Ansichten, Middleware und App-Struktur nutzen, um die Abhängigkeitsinjektion manuell oder mit der Bibliothek „
dependency-injector“ einzubauen.Hier ist eine Schritt-für-Schritt-Anleitung, wie du Dependency Injection in Django einbaust.
Schritt 1: Deinen Service festlegen
Erstell einen einfachen Dienst mit deiner Geschäftslogik in einer neuen Datei „
services.py” in deinem App-Ordner.Schritt 2: Richte den Dependency-Injector-Container ein
Erstell in deinem Anwendungsverzeichnis einen Container mit der Bibliothek „
dependency-injector” in einer neuen Datei „containers.py”.Schritt 3: Erstell eine Ansicht mit eingebundenen Abhängigkeiten
Verwende den Dekorator „
@inject” in deiner Ansicht, um Abhängigkeiten in „views.py” zu erhalten.Schritt 4: Aktualisiere die Datei „urls.py“
Geh in deinem Projektordner zur Datei „
urls.py“ und hänge deinen Abhängigkeitsinjektor in das URL-Routing-System von Django ein.Schritt 5: Verbinde den Container in apps.py
Geh auf „
apps.py“ und verbinde den Container mit der App.Geh dann zu deiner App-Konfiguration unter „
INSTALLED_APPS“ und füge deine Anwendung hinzu.Schritt 6: Die Ansicht testen
Starte deinen Django-Server und ruf die Ansicht über die URL
http://localhost:8000/greet/?name=Djangoauf.Du solltest jetzt Folgendes sehen.
Abhängigkeitsinjektion in FastAPI
FastAPI hat eine eingebaute Unterstützung für die Abhängigkeitsinjektion über den speziellen Marker „
Depends()“, der alles injiziert, was eine Funktion zurückgibt.Hier ist eine Schritt-für-Schritt-Anleitung, wie du die Abhängigkeitsinjektion in FastAPI machst.
Schritt 1: Einen Service definieren
Schritt 2: Erstell die Abhängigkeitsfunktion
Schritt 3: Richte die FastAPI-App ein und nutze Dependency Injection.
Schritt 4: Starte die App und probier sie aus
Starte deinen Server mit dem Befehl „
uvicorn main:app –reload“ und ruf die URL „http://localhost:8000/greet?name=FastAPI“ auf. Du solltest die folgende Antwort sehen.Vergleich von Python-Frameworks für die Abhängigkeitsinjektion
Es gibt verschiedene Python-Abhängigkeits-Frameworks, die alle ihre eigenen Vorteile, Syntax und Anwendungsfälle haben. Welches Framework du nimmst, hängt von der Größe und Struktur deines Projekts ab.
Hier ist ein Vergleich einiger gängiger Python-Frameworks für die Abhängigkeitsinjektion.
Rahmen
Beste Grüße Für
Stärken
Einschränkungen
dependency-injectorKomplexe Anwendungen in der Produktion
Voll ausgestattet, schnell und anpassbar
Ausführlich
injectorEinfach und perfekt für kleine Anwendungen
Klare Syntax
Nicht so weit entwickelt wie andere.
pinjectErweiterte Bindungslogik
Automatische Verdrahtung
Langsamer wegen Selbstreflexion
punqEinfache Anwendungen
Minimal und schnell
Im Vergleich zu anderen Produkten eingeschränkte Funktionen
FastAPI
Depends()FastAPI-Anwendungen
Integrierte Unterstützung für Async, außerdem testbar
Nicht außerhalb von FastAPI wiederverwendbar
Flask-injectorFlask-Anwendungen
Einfache Integration in Flask-Anwendungen
Hängt davon ab
injectordjango-injectorDjango-Projekte
Bietet Abhängigkeitsinjektion für Ansichten und Middleware.
Hängt davon ab
injectorFortgeschrittene Implementierungsmuster
Wenn dein Anwendungscode immer größer wird, wird es echt schwierig, die Abhängigkeitsinjektion zu verwalten.
Mit der erweiterten Abhängigkeitsinjektion kannst du Aufgaben wie Ressourcen im Anforderungsbereich, Bereinigung und Trennung von Anliegen erledigen.
Abhängigkeiten mit Gültigkeitsbereich
Mit bereichsbezogenen Abhängigkeiten kannst du steuern, wie lange eine Abhängigkeit bestehen bleibt, z. B. wenn du ein neues Objekt pro Anfrage, ein gemeinsames Objekt für die gesamte Lebensdauer der App oder die automatische Bereinigung von Ressourcen wie Datenbankverbindungen möchtest.
Die meisten Dependency-Injector-Frameworks unterstützen Bereiche wie:
Hier ist ein Beispiel für bereichsbezogene Abhängigkeiten mit verschachtelten Containern, die „
dependency-injector“ verwenden.Im obigen Beispiel macht „
MyResource“ eine Klasse nach, die Setup und Teardown braucht, wie zum Beispiel das Öffnen/Schließen einer Datei oder Datenbank.Die „
container” stellt die Ressource über „providers.Resource” bereit, und wenn du „with container.resource()” verwendest, wird der Kontext korrekt aufgerufen und verlassen.Dieses Muster sorgt dafür, dass Ressourcen wie Datei-Handles, Datenbankverbindungen oder Netzwerksitzungen sauber verwaltet werden.
Stell dir einen API-Endpunkt vor, der für die aktuelle Anfrage eine Datenbanksitzung oder einen Logger mit anfragespezifischem Kontext braucht.
Bei bereichsbezogenen Abhängigkeiten erstellt das Framework einen Container für den Anforderungsbereich, wenn die Anforderung beginnt, alle Abhängigkeiten aus diesem Bereich aufzulösen.
Wenn die Anfrage beendet ist, werden alle notwendigen Aufräumarbeiten, wie das Schließen von Datenbanksitzungen, automatisch erledigt. Dadurch wird sichergestellt, dass Zustände nicht über Anfragen hinweg geteilt werden und keine Ressourcenlecks entstehen.
Asynchrone Injektion
Um Aufgaben, die viel mit Ein- und Ausgabe zu tun haben, effizient zu erledigen, brauchst du eine asynchrone Ausführung. Abhängigkeitsinjektionssysteme in Frameworks wie FastAPI und
dependency-injectorbieten asynchrone Injektion überasync/await.async defasync resourcesAsynchrone Injektion bedeutet, Abhängigkeiten, die asynchrone Funktionen sind, einzufügen oder asynchrone Datenbankverbindungen, externe APIs oder Hintergrundaufgaben zu verwalten.Anstatt sie synchron aufzulösen, wartet das Abhängigkeitsinjektionssystem, bis sie aufgelöst sind, bevor es die Injektion durchführt.
Hier ist ein Beispiel für eine asynchrone Abhängigkeit in FastAPI:
FastAPI kümmert sich automatisch um die asynchrone Auflösung von „
get_user“ und fügt das Ergebnis ein.dependency-injectorunterstützt auch die asynchrone Lebenszyklusverwaltung mithilfe von Anwendungsdienstanbietern (AsyncResource).Im obigen Code:
resources.AsyncResourcewird für die asynchrone Einrichtungs-/Abbau-Logik verwendet.await container.init_resources()initialisiert alle Ressourcen und ruft „init“ auf.await container.shutdown_resources()räumt alle Ressourcen auf.await container.db()holt die eigentliche Ressourceninstanz, alsoAsyncDB.Mit der asynchronen Abhängigkeitsinjektion bleibt deine App leistungsstark, weil synchrone I/O in asynchronen Routen vermieden werden und du nicht mehr manuell auf Abhängigkeiten in deiner Logik warten musst.
Sicherheitsauswirkungen
Abhängigkeitsinjektion macht zwar alles modularer und testbarer, kann aber auch Sicherheitsrisiken mit sich bringen, wenn man nicht aufpasst.
Ein großes Risiko ist die Verwirrung bei Abhängigkeiten, wenn bösartige Pakete legitime Pakete imitieren. Zum Beispiel haben Angreifer Pakete auf PyPI, die Namen aus internen Unternehmenssystemen haben, um Apps dazu zu bringen, die bösartige Version zu nutzen.
Um auf der sicheren Seite zu bleiben, solltest du darauf achten, dass du niemals nicht vertrauenswürdige oder vom Benutzer kontrollierte Komponenten einfügst und Konfigurationsquellen wie Umgebungsvariablen vor dem Einfügen überprüfst.
Verwende die Dateien „
requirements.txt“ oder „poetry.lock“, um bestimmte Versionen zu sperren und Sicherheitsscans bei Änderungen an Abhängigkeiten automatisch durchzuführen. Außerdem solltest du deine Abhängigkeiten regelmäßig auf Schwachstellen überprüfen.Die zentrale Verwaltung deiner Abhängigkeiten ist auch wichtig, weil sie dafür sorgt, dass alles in deinen Anwendungen gleich läuft und Sicherheits-Patches oder Versions-Updates einfacher installiert werden können. Das hilft dabei, „Schattenabhängigkeiten” zu vermeiden, die auf verschiedene Arten in den Code eingeschleust werden können.
Es gibt ein paar Tools, die Risiken erkennen und deine Sicherheit verbessern können:
Praktische Anwendungen
Die Abhängigkeitsinjektion ist nicht nur so ein theoretisches Muster, sondern wird in vielen Produktionssystemen eingesetzt, um die Komplexität zu verwalten, die Flexibilität zu verbessern und das Testen zu optimieren.
Webdienst-Einstellungen
In modernen Web-Apps ist die Abhängigkeitsinjektion echt wichtig für die Verwaltung von Standarddiensten wie Authentifizierung, Protokollierung und Datenbankzugriff.
Frameworks wie FastAPI sind echt wichtig, um Routen und Abhängigkeiten zu klären.
Das folgende Beispiel zeigt, wie man eine zentralisierte Authentifizierung in FastAPI macht.
Im obigen Code wird „
get_current_user()” als Routenabhängigkeit eingefügt, aber die Logik zum Überprüfen des Tokens ist zentralisiert und kann wiederverwendet werden.Du kannst das auch für den Datenbankzugriff machen, sodass die Datenbanksitzungen automatisch verwaltet und bereinigt werden.
Hier ist ein Beispiel, wie du mit Abhängigkeitsinjektion den Datenbankzugriff zentralisieren kannst.
Im obigen Code ist „
get_db_session” ein Abhängigkeitsanbieter. Wenn die Route „read_items“ aufgerufen wird, fügt FastAPI automatisch den Parameter „db“ ein, sodass die Route auf die Datenbank zugreifen kann, ohne dass die Sitzung manuell eingerichtet oder beendet werden muss.Entwicklung mit Tests
Mit der Abhängigkeitsinjektion kannst du ganz einfach die Abhängigkeiten ändern, die Komponenten bekommen, was beim Testen echt wichtig ist.
Du kannst echte Dienste während des Testens durch Mocks ersetzen,wodurch dein Code modularer und einfacher zu testen ist.
FastAPI und „
dependency-injector” können Abhängigkeiten beim Testen überschreiben.Hier ist ein Beispiel in FastAPI.
Im obigen Code-Beispiel wird „
get_db_session“ während der Tests mit einer Scheindatenbank überschrieben, sodass der Produktionscode nicht gepatcht oder geändert werden muss.Das Gleiche gilt auch für „
depedency-injector“. Du kannst deine echte Datenbank für Testzwecke mit einer Scheindatenbank überschreiben, wie im folgenden Beispiel gezeigt.Die Methode „
override()“ ersetzt die echte Abhängigkeit durch eine Scheinabhängigkeit, die sauber, kontrollierbar und reversibel ist.Hier sind ein paar Tipps, die du bei der Überschreibung von Abhängigkeiten beachten solltest:
FastAPI.dependency_overrides“ in FastAPI oder „dependency_injector.override()“ in „dependency-injector“.pytest, um wiederverwendbare Mocks zu erstellen und sie über Fixtures einzufügen.Bewährte Vorgehensweisen und Anti-Muster
Obwohl die Abhängigkeitsinjektion den Code modular und leicht testbar machen soll, kann sie zu Fehlern führen oder den Code kompliziert machen.
Empfohlene Vorgehensweisen
Hier sind ein paar Tipps, die du beim Erstellen von Abhängigkeiten beachten solltest:
Häufige Fehler
Hier sind ein paar häufige Fehler, die du bei der Verwendung von Abhängigkeitsinjektion vermeiden solltest.
Testen von Abhängigkeitsinjektionen in Python
Mit der Abhängigkeitsinjektion kannst du ganz einfach Mock-Objekte in deine Komponenten einfügen, was Unit-Tests einfacher macht. So kannst du grundlegende Teile von testspezifischen Teilen trennen.
Mit der Abhängigkeitsinjektion sind Tests deterministisch, haben keine Nebeneffekte und führen zu einer schnelleren Testausführung.
Hier ist ein Beispiel, das zeigt, wie du mit Abhängigkeitsinjektion Mock-, Stub- oder Fake-Versionen dieser Abhängigkeiten in deine Tests einfügen kannst.
Ersetze in Tests „
EmailService“ durch ein Mock:Achte darauf, dass du oft Abhängigkeitsinjektion verwendest und Monkey Patching vermeidest, um die Wartbarkeit zu verbessern und zu verhindern, dass es bei internen Namensänderungen zu Problemen kommt.
Fazit
Auch wenn du für kleine Projekte vielleicht nicht gleich eine Abhängigkeitsinjektion brauchst, kannst du mit zunehmender Größe und Skalierung des Projekts von der Struktur und Modularität profitieren, die die Abhängigkeitsinjektion bietet.
Die Zukunft der Abhängigkeitsinjektion in Python sieht vielversprechend aus und wir können davon ausgehen, dass sie mit einem verbesserten Typsystem und einer optimierten Verwaltung des asynchronen Lebenszyklus weiterentwickelt wird.
Das Beherrschen der Abhängigkeitsinjektion ist wichtig, um ein guter Python-Entwickler zu werden. Aber das ist noch nicht alles: Viele andere fortgeschrittene Konzepte können deine Python-Kenntnisse noch weiter verbessern.
Um dir beim Lernen zu helfen, findest du hier ein paar Ressourcen, die du dir mal anschauen solltest.
Kurse:
Blog-Beiträge: