Python Typannotationen und Generics einfach erklärt
Typannotationen und Generics in Python erklärt: So nutzt du Type Hints für lesbaren, wartbaren Code – mit Beispielen für Einsteiger.
Von Marcel Schmidtpeter 02. Sept. 2026
Du öffnest ein Python-Projekt von einem Kollegen und findest diese Funktion:
def verarbeite_daten(daten):
return daten[0]Was kommt hier rein? Eine Liste? Ein Dictionary? Und was kommt raus? Niemand verrät es dir - du musst den kompletten Code drumherum lesen. Genau für dieses Problem gibt es Typannotationen (englisch: Type Hints). Und wenn du sie richtig nutzt, kommst du automatisch bei Generics raus. Beide Konzepte erklären wir dir hier Schritt für Schritt.
Was sind Typannotationen in Python?
Typannotationen sind Hinweise im Code, die beschreiben, welchen Datentyp eine Variable, ein Funktionsparameter oder ein Rückgabewert haben soll. Typannotationen gibt es offiziell seit Python 3.5 - definiert in PEP 484.
So sieht der Unterschied aus:
# Ohne Typannotationen
def addiere(a, b):
return a + b
# Mit Typannotationen
def addiere(a: int, b: int) -> int:
return a + bDie Syntax ist einfach: Nach dem Parameternamen kommt ein Doppelpunkt, dann der Typ. Der Rückgabetyp steht nach dem Pfeil `->` am Ende der Funktionsdefinition.
Merke: Typannotationen sind nur Hinweise. Python prüft sie zur Laufzeit nicht. Du kannst trotzdem `addiere("a", "b")` aufrufen, ohne dass ein Fehler passiert.
Warum dann überhaupt annotieren? Weil drei Gruppen davon profitieren:
Du selbst, weil der Code selbstdokumentierend wird
Deine IDE (z. B. PyCharm oder VS Code), die dir Autovervollständigung und Warnungen liefert
Typprüfer wie mypy, die deinen Code statisch analysieren - also vor dem Ausführen
Gerade in der Ausbildung, im Studium oder im ersten Job bekommst du dafür Pluspunkte: Teams erwarten Type Hints in größeren Projekten meist als Standard.
Welche Typen kannst du annotieren?
Für einfache Datentypen wie `int` oder `str` schreibst du den Typ direkt hin. Für komplexe Datentypen nutzt du eckige Klammern, um den Inhaltstyp anzugeben. Wenn du die Grundlagen zu Listen und Dictionaries auffrischen willst, wirf einen Blick in unseren Überblick zu Python Collections.
Annotation | Bedeutung | Beispiel-Wert |
`int` | Ganzzahl | `42` |
`str` | Zeichenkette | `"Hallo"` |
`float` | Kommazahl | `3.14` |
`bool` | Wahrheitswert | `True` |
`list[int]` | Liste von Ganzzahlen | `[1, 2, 3]` |
`dict[str, int]` | Dictionary: Text-Schlüssel, Zahl-Werte | `{"alter": 25}` |
`tuple[int, str]` | Tupel mit fester Struktur | `(1, "a")` |
`int \ | None` | Zahl oder `None` |
Der Typ `int | None` (gesprochen: "int oder None") brauchte früher das `Optional[int]` aus dem `typing`-Modul. Seit Python 3.10 reicht das Pipe-Zeichen - deutlich lesbarer.
Ein komplettes Beispiel:
def zaehle_woerter(text: str) -> int:
return len(text.split())
def finde_nutzer(namen: list[str], index: int) -> str | None:
if index < len(namen):
return namen[index]
return NoneBeachte: `finde_nutzer` kann auch `None` zurückgeben, weil der Index außerhalb der Liste liegen kann. Genau solche Fälle macht `str | None` explizit sichtbar - für dich und für deine IDE.
Was sind Generics in Python?
Generics sind Typen mit Platzhaltern, die erst bei der Verwendung durch einen konkreten Typ ersetzt werden. Stell dir ein Formular vor, in dem "Name: ____" steht - der Platzhalter wird später ausgefüllt. Bei Generics füllt der Typ `T` diese Lücke aus.
Das Problem: Du willst eine Funktion schreiben, die das erste Element einer Liste zurückgibt. Egal ob Zahlen, Namen oder Bestellungen. Mit `list[str]` wärst du auf Zeichenketten festgelegt. Die Lösung ist ein TypeVar - die Variable für einen Typ:
from typing import TypeVar
T = TypeVar("T")
def erstes_element(liste: list[T]) -> T:
return liste[0]
zahl = erstes_element([10, 20, 30]) # T ist jetzt int
name = erstes_element(["Anna", "Ben"]) # T ist jetzt strDas Geniale: Beim Aufruf mit `list[int]` "weiß" der Typprüfer, dass `zahl` ein `int` ist - und beim Aufruf mit `list[str]`, dass `name` ein `str` ist. Ein Code, typsicher für alle Datentypen.
Seit Python 3.12 geht das dank PEP 695 sogar ohne den Import:
def erstes_element[T](liste: list[T]) -> T:
return liste[0]Merke: `T` ist kein "irgendein Typ, alles darf gemischt werden". Innerhalb eines Aufrufs muss `T` überall derselbe Typ sein. Wer eine `list[int]` übergibt, bekommt ein `int` zurück - nicht plötzlich ein `str`.
Praxisbeispiel: Eine generische Stack-Klasse
Klassiker aus Ausbildung und Studium: ein Stack (Stapel-Datenstruktur, LIFO-Prinzip - Last In, First Out). Mit Generics baust du ihn einmal - für alle Typen.
from typing import TypeVar, Generic
T = TypeVar("T")
class Stack(Generic[T]):
def __init__(self):
self._elemente: list[T] = []
def push(self, element: T) -> None:
self._elemente.append(element)
def pop(self) -> T:
if self.ist_leer():
raise IndexError("Stack ist leer")
return self._elemente.pop()
def ist_leer(self) -> bool:
return len(self._elemente) == 0Die Klasse erbt von `Generic[T]` und deklariert damit: "Ich arbeite mit einem Typ, den der Nutzer beim Erzeugen festlegt." So nutzt du sie:
zahlen: Stack[int] = Stack[int]()
zahlen.push(1)
zahlen.push(2)
print(zahlen.pop()) # 2
namen: Stack[str] = Stack[str]()
namen.push("Anna")
print(namen.pop()) # AnnaDeine IDE erkennt: `zahlen.pop()` liefert ein `int`, `namen.pop()` ein `str`. Wenn du danach versuchst, `zahlen.pop() + "x"` zu schreiben, warnt dich der Typprüfer sofort. Die offizielle Dokumentation dazu findest du im `typing`-Modul der Python-Docs.
Welche 3 Anfängerfehler solltest du vermeiden?
Fehler 1: Glauben, dass Python die Typen durchsetzt. Type Hints sind reine Deklarationen. `addiere("a", "b")` läuft ohne Fehlermeldung durch - obwohl laut Annotation nur `int` erlaubt ist. Wer Typen wirklich prüfen will, braucht ein Tool wie mypy:
pip install mypy
mypy mein_skript.pyFehler 2: `None` als Standardwert ohne Typ-Anpassung. Häufiger Stolperstein:
# Falsch: None passt nicht zu str
def begruessung(name: str = None) -> str:
return "Hallo " + name
# Richtig
def begruessung(name: str | None = None) -> str:
if name is None:
return "Hallo Gast"
return "Hallo " + nameFehler 3: `list[int]` in alten Python-Versionen nutzen. Die eingebaute Schreibweise mit eckigen Klammern gibt es erst seit Python 3.9. Davor musste man `List[int]` aus dem `typing`-Modul importieren. Wenn du in einer Firma noch auf Python 3.8 arbeitest, knallt `list[int]` mit einem `TypeError`. Die neue `def f[T]()`-Syntax wiederum braucht Python 3.12. Immer kurz checken, welche Version im Projekt läuft.
Dein Übungstipp für heute
Nimm ein eigenes Skript - oder eines aus deinen Lernunterlagen - und annotiere jede Funktion: Parameter und Rückgabewert. Danach führe `mypy` aus und schau, welche Fehler er findet. Du wirst überrascht sein, wie viele versteckte `None`-Fälle und Typ-Mixe in deinem eigenen Code schlummern. Das ist übrigens auch eine beliebte Frage in Prüfungen und Vorstellungsgesprächen.
FAQ: Häufige Fragen zu Typannotationen und Generics
Werden Typannotationen in Python zur Laufzeit geprüft?
Nein. Python bleibt dynamisch typiert - Type Hints sind nur Hinweise für Menschen, IDEs und statische Analysetools wie mypy oder Pyright.
Seit wann gibt es `list[int]` statt `List[int]`?
Seit Python 3.9. Davor musste `List` aus dem `typing`-Modul importiert werden. Seit Python 3.10 gibt es zusätzlich `int | None` statt `Optional[int]`.
Was bedeutet `T` bei Generics?
`T` ist eine Typvariable (TypeVar) - ein Platzhalter, der beim Aufruf durch einen konkreten Typ ersetzt wird. Innerhalb eines Aufrufs bleibt er konsistent.
Brauche ich Typannotationen in jedem Projekt?
Nein, sie sind optional. In kleinen Skripten verzichtet man oft darauf. In größeren Projekten und Teams sind sie aber Quasi-Standard, weil sie Fehler früh sichtbar machen.
Was ist der Unterschied zwischen `Optional[int]` und `int | None`?
Kein inhaltlicher - beide bedeuten "int oder None". Die Pipe-Schreibweise ist seit Python 3.10 die moderne, lesbare Variante.
Fazit
Typannotationen machen deinen Python-Code lesbar und werkzeugfreundlich, ohne die dynamische Natur von Python aufzugeben. Generics gehen einen Schritt weiter: eine Funktion oder Klasse schreiben, typsicher für alle Datentypen nutzen. Starte mit einfachen Annotationen wie `str` und `int`, arbeite dich zu `list[T]` und generischen Klassen vor - und lass mypy regelmäßig drüberschauen.
Wenn du dich gezielt auf Prüfungen, Abschlussarbeiten oder dein erstes Job-Interview vorbereiten willst: Bei study-it.education bekommst du individuelle Nachhilfe für Python, Java, C# und alle Themen rund um deine IT-Ausbildung oder dein Studium. Schau vorbei und lerne mit Leuten, die sich auch mit `__str__` und `__eq__` auskennen - mehr dazu in unserem Artikel zu Python Dunder-Methoden.
1:1-Hilfe zu diesem Thema?
Brauchst du jemand, der das mit dir durchgeht?
Statt nochmal lesen: 30 min mit einem Senior-Dev.
Kostenloses Erstgespräch buchen →
Typannotationen und Generics in Python erklärt: So nutzt du Type Hints für lesbaren, wartbaren Code – mit Beispielen für Einsteiger.
marcel.schmidtpeter@study-it.education →Mehr Lesestoff
Python None Optional: None-Werte sicher behandeln – einfach erklärt
None-Werte sicher behandeln: Erfahre, wie Optional und Typannotationen in Python funktionieren und wie du typische None-Fehler vermeidest.
Python List Comprehension map filter: Einfach erklärt mit Beispielen
List Comprehensions, map und filter im Vergleich: So filterst und transformierst du Daten in Python – mit anschaulichen Beispielen für den Einstieg.
Python Lambda Funktion einfach erklärt: Syntax & Beispiele
Lambda-Funktionen in Python: Erfahre, wie du anonyme Einzeiler-Funktionen schreibst und wann du sie sinnvoll einsetzt – mit Beispielen.