Kurs
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
class EmailService:
def send_email(self, message):
print(f"Sending email: {message}")
class UserNotifier:
def __init__(self):
self.email_service = EmailService() # Creates its own dependency
def notify(self, message):
self.email_service.send_email(message)
notifier = UserNotifier()
notifier.notify("Welcome!")
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.
class EmailService:
def send_email(self, message):
print(f"Sending email: {message}")
class UserNotifier:
def __init__(self, email_service):
self.email_service = email_service # Dependency is injected
def notify(self, message):
self.email_service.send_email(message)
email_service = EmailService()
notifier = UserNotifier(email_service)
notifier.notify("Welcome!")
Die obige Version ist flexibel und lässt verschiedene Messaging-Dienste einbinden. Außerdem kannst du EmailService in 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-injector Rahmen bietet einen strukturierten Ansatz.
Container-Anbieter-Architektur
Das Framework „ dependency-injector “ funktioniert mit einer Container-Provider-Architektur.
- Behälter: Das ist die zentrale Stelle für die Abhängigkeiten deiner Anwendung.
- Anbieter: Hier wird festgelegt, wie deine Abhängigkeiten erstellt werden.
- Einstellungen: Damit kannst du Einstellungen ändern und in Objekte einfügen.
- Überschreiben: Damit kannst du Abhängigkeiten austauschen, ohne den Anwendungscode zu ändern, vor allem beim Testen.
from dependency_injector import containers, providers
# Services
class EmailService:
def send_email(self, message):
print(f"Sending email: {message}")
class UserNotifier:
def __init__(self, email_service):
self.email_service = email_service
def notify(self, message):
self.email_service.send_email(message)
# Container
class Container(containers.DeclarativeContainer):
email_service = providers.Singleton(EmailService)
user_notifier = providers.Factory(UserNotifier, email_service=email_service)
# Usage
container = Container()
notifier = container.user_notifier()
notifier.notify("Hello!")
Im obigen Beispiel:
-
Containerlegt fest, wie Instanzen erstellt werden -
Singletonsorgt dafür, dass nur eine Instanz von „EmailService” existiert -
FactoryErstellt 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 services
class EmailService:
def send_email(self, message):
print(f"Sending email: {message}")
class UserNotifier:
def __init__(self, email_service: EmailService):
self.email_service = email_service
def notify(self, message):
self.email_service.send_email(message)
# Create container
class Container(containers.DeclarativeContainer):
email_service = providers.Singleton(EmailService)
user_notifier = providers.Factory(UserNotifier, email_service=email_service)
from dependency_injector.wiring import inject, Provide
@inject
def main(notifier: UserNotifier = Provide[Container.user_notifier]):
notifier.notify("Hello!")
if __name__ == "__main__":
container = Container()
container.wire(modules=[__name__]) # This wires the current module
main()
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
class GreetingService:
def get_greeting(self, name):
return f"Hello, {name}!"
Schritt 2: Richte deinen Dependency-Injection-Container ein
from dependency_injector import containers, providers
class Container(containers.DeclarativeContainer):
greeting_service = providers.Factory(GreetingService)
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")
@inject
def greet(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)
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.py
class GreetingService:
def get_greeting(self, name):
return f"Hello, {name}!"
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 ”.
# myapp/containers.py
from dependency_injector import containers, providers
from .services import GreetingService
class Container(containers.DeclarativeContainer):
greeting_service = providers.Factory(GreetingService)
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.py
from django.http import HttpResponse
from dependency_injector.wiring import inject, Provide
from .containers import Container
from .services import GreetingService
@inject
def greet_view(
request, greeting_service: GreetingService = Provide[Container.greeting_service]
):
name = request.GET.get("name", "World")
message = greeting_service.get_greeting(name)
return HttpResponse(message)
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),
]
Schritt 5: Verbinde den Container in apps.py
Geh auf „ apps.py “ und verbinde den Container mit der App.
# myapp/apps.py
from django.apps import AppConfig
from .containers import Container
class MyAppConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "myapp"
def ready(self):
container = Container()
container.wire(modules=["myapp.views"])
Geh dann zu deiner App-Konfiguration unter „ INSTALLED_APPS “ und füge deine Anwendung hinzu.
# settings.py
INSTALLED_APPS = [
'myapp.apps.MyAppConfig',
...
]
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
class GreetingService:
def get_greeting(self, name: str) -> str:
return f"Hello, {name}!"
Schritt 2: Erstell die Abhängigkeitsfunktion
def get_greeting_service():
return GreetingService()
Schritt 3: Richte die FastAPI-App ein und nutze Dependency Injection.
app = FastAPI()
@app.get("/greet")
def greet(
name: str = "World", service: GreetingService = Depends(get_greeting_service)
):
return {"message": service.get_greeting(name)}
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 |
|
|
Komplexe Anwendungen in der Produktion |
Voll ausgestattet, schnell und anpassbar |
Ausführlich |
|
Einfach und perfekt für kleine Anwendungen |
Klare Syntax |
Nicht so weit entwickelt wie andere. |
|
|
Erweiterte Bindungslogik |
Automatische Verdrahtung |
Langsamer wegen Selbstreflexion |
|
|
Einfache Anwendungen |
Minimal und schnell |
Im Vergleich zu anderen Produkten eingeschränkte Funktionen |
|
|
FastAPI-Anwendungen |
Integrierte Unterstützung für Async, außerdem testbar |
Nicht außerhalb von FastAPI wiederverwendbar |
|
|
Flask-Anwendungen |
Einfache Integration in Flask-Anwendungen |
Hängt davon ab |
|
|
Django-Projekte |
Bietet Abhängigkeitsinjektion für Ansichten und Middleware. |
Hängt davon ab |
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 cleanup
class MyResource:
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 resource
class MyContainer(containers.DeclarativeContainer):
resource = providers.Resource(MyResource)
# Using the resource with a context manager
container = MyContainer()
with container.resource() as r:
print("Using the resource")
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 def async 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 dependency
async def get_user():
# Simulate async I/O
await some_async_db_query()
return {"name": "Philip Jones"}
@app.get("/profile")
async def read_profile(user: dict = Depends(get_user)):
return {"user": user}
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
class AsyncDB:
async def connect(self):
print("Connected")
return self
async def disconnect(self):
print("Disconnected")
class AsyncDBResource(resources.AsyncResource):
async def init(self) -> AsyncDB:
db = AsyncDB()
return await db.connect()
async def shutdown(self, db: AsyncDB):
await db.disconnect()
class Container(containers.DeclarativeContainer):
db = providers.Resource(AsyncDBResource)
# Usage
async def main():
container = Container()
await container.init_resources()
db = await container.db() # Get the AsyncDB instance
# Use db here...
await container.shutdown_resources()
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:
- Snyk: Sucht nach bekannten Schwachstellen in Python-Paketen und schlägt Lösungen vor.
- Xygeni: Bietet Schutz für CI/CD-Pipelines.
- 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
def get_current_user(token: str = Depends(oauth2_scheme)):
user = verify_token(token)
if not user:
raise HTTPException(status_code=401)
return user
@app.get("/protected")
def protected_route(user: dict = Depends(get_current_user)):
return {"message": f"Hello {user['name']}"}
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
async def get_db_session() -> AsyncSession:
async with async_session() as session:
yield session
@app.get("/items")
async def read_items(db: AsyncSession = Depends(get_db_session)):
return await db.execute("SELECT * FROM items")
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
def override_db():
return TestDBSession()
app.dependency_overrides[get_db_session] = override_db
client = TestClient(app)
response = client.get("/items")
assert response.status_code == 200
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
def test_service_behavior():
container = Container()
container.db.override(providers.Factory(TestDB))
service = container.service()
assert service.get_data() == "mocked"
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.
class EmailService:
def send_email(self, to: str, message: str): ...
class Notifier:
def __init__(self, email_service: EmailService):
self.email_service = email_service
def alert(self, user_email):
self.email_service.send_email(user_email, "Alert!")
Ersetze in Tests „ EmailService “ durch ein Mock:
class TestEmailService:
def send_email(self, to, message):
self.sent_to = to
def test_notifier_sends_email():
test_email = TestEmailService()
notifier = Notifier(test_email)
notifier.alert("test@example.com")
assert test_email.sent_to == "test@example.com"
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:
- Python-Pakete entwickeln
- Einführung in PySpark
- Objektorientierte Programmierung in Python
- Objektorientierte Programmierung für Fortgeschrittene in Python
Blog-Beiträge:
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 def async 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.

