Structural Pattern Matching in Python: match und case verstehen

Seit Python 3.10 gibt es mit match und case ein mächtiges Werkzeug, das weit mehr kann als ein einfaches switch. Lerne, wie du damit Werte, Strukturen und ganze Objekte elegant abgleichst.

Teilen

Wenn du aus anderen Sprachen kommst, hast du dir in Python vielleicht schon ein switch-Statement gewünscht. Lange Zeit gab es das nicht – man behalf sich mit langen if/elif-Ketten oder Dictionaries. Seit Python 3.10 gibt es aber etwas viel Mächtigeres: Structural Pattern Matching mit den Schlüsselwörtern match und case. In diesem Beitrag schauen wir uns an, wie es funktioniert und warum es mehr ist als nur ein einfaches switch.

Der einfachste Fall: Werte abgleichen

Im Grunde vergleicht match einen Ausdruck mit mehreren Mustern (englisch: patterns) und führt den ersten passenden Zweig aus. So sieht das für einfache Werte aus:

def http_status(code):
    match code:
        case 200:
            return "OK"
        case 404:
            return "Nicht gefunden"
        case 500:
            return "Serverfehler"
        case _:
            return "Unbekannter Status"

print(http_status(404))  # Nicht gefunden
print(http_status(999))  # Unbekannter Status

Das case _: am Ende ist der sogenannte Wildcard. Er passt auf alles und übernimmt damit die Rolle eines default-Zweigs. Wichtig: Anders als bei if/elif wird immer nur ein passender case ausgeführt – ein break wie in anderen Sprachen brauchst du nicht.

Mehrere Werte und Bedingungen kombinieren

Du kannst mit dem |-Operator mehrere Werte in einem Muster zusammenfassen. Zusätzlich lässt sich mit einem if eine sogenannte Guard anhängen, die eine weitere Bedingung prüft:

def bewerte_zahl(n):
    match n:
        case 0:
            return "Null"
        case 1 | 2 | 3:
            return "klein"
        case _ if n < 0:
            return "negativ"
        case _:
            return "groß"

print(bewerte_zahl(2))   # klein
print(bewerte_zahl(-5))  # negativ
print(bewerte_zahl(42))  # groß

Die Guard case _ if n < 0 greift nur, wenn das Muster passt und die Bedingung wahr ist. So kombinierst du strukturelles Matching mit klassischer Logik.

Strukturen zerlegen: Listen und Tupel

Hier wird es spannend, denn jetzt geht es um das namensgebende structural matching. Du kannst nicht nur Werte, sondern auch die Struktur von Daten abgleichen und dabei einzelne Teile direkt in Variablen entpacken:

def beschreibe_punkt(punkt):
    match punkt:
        case (0, 0):
            return "Ursprung"
        case (x, 0):
            return f"Auf der X-Achse bei {x}"
        case (0, y):
            return f"Auf der Y-Achse bei {y}"
        case (x, y):
            return f"Punkt bei ({x}, {y})"
        case _:
            return "Kein gültiger Punkt"

print(beschreibe_punkt((0, 0)))   # Ursprung
print(beschreibe_punkt((3, 0)))   # Auf der X-Achse bei 3
print(beschreibe_punkt((2, 5)))   # Punkt bei (2, 5)

Im Muster case (x, 0) wird der erste Wert an die Variable x gebunden, während der zweite Wert exakt 0 sein muss. Bei Listen kannst du mit *rest sogar variable Längen abfangen:

def erster_und_rest(items):
    match items:
        case []:
            return "leer"
        case [einziges]:
            return f"nur ein Element: {einziges}"
        case [erstes, *rest]:
            return f"erstes: {erstes}, restliche: {rest}"

print(erster_und_rest([]))          # leer
print(erster_und_rest([1]))         # nur ein Element: 1
print(erster_und_rest([1, 2, 3]))   # erstes: 1, restliche: [2, 3]

Objekte und Dictionaries matchen

Richtig nützlich wird das Ganze bei komplexeren Datenstrukturen. Du kannst Klassen-Attribute und Dictionary-Schlüssel direkt im Muster abfragen. Das eignet sich hervorragend, um zum Beispiel JSON-artige Daten oder Objekte zu verarbeiten:

from dataclasses import dataclass

@dataclass
class Kreis:
    radius: float

@dataclass
class Rechteck:
    breite: float
    hoehe: float

def flaeche(form):
    match form:
        case Kreis(radius=r):
            return 3.14159 * r * r
        case Rechteck(breite=b, hoehe=h):
            return b * h
        case _:
            raise ValueError("Unbekannte Form")

print(flaeche(Kreis(radius=2)))          # 12.56636
print(flaeche(Rechteck(breite=3, hoehe=4)))  # 12

Python prüft hier automatisch den Typ des Objekts und bindet die passenden Attribute an Variablen. Auch verschachtelte Dictionaries lassen sich so elegant zerlegen:

def verarbeite_event(event):
    match event:
        case {"typ": "klick", "x": x, "y": y}:
            return f"Klick bei ({x}, {y})"
        case {"typ": "taste", "code": code}:
            return f"Taste gedrückt: {code}"
        case _:
            return "Unbekanntes Event"

print(verarbeite_event({"typ": "klick", "x": 10, "y": 20}))
# Klick bei (10, 20)

Ein Dictionary-Muster passt übrigens auch dann, wenn das Dictionary mehr Schlüssel enthält als angegeben – es müssen nur die genannten Schlüssel vorhanden sein.

Wann solltest du match verwenden?

Structural Pattern Matching ist kein Ersatz für jedes if. Für eine einzelne Bedingung bleibt ein normales if klarer. Seine Stärke spielt match aus, wenn du eine Datenstruktur nach ihrer Form unterscheiden möchtest – etwa beim Parsen von Nachrichten, beim Verarbeiten von Events oder beim Auswerten unterschiedlicher Objekttypen. Der Code wird dadurch oft deutlich lesbarer als eine verschachtelte if/elif-Kette mit vielen isinstance-Prüfungen.

Fazit

Mit match und case hat Python ein modernes, ausdrucksstarkes Werkzeug bekommen, das weit über ein klassisches switch hinausgeht. Du kannst damit Werte abgleichen, Strukturen zerlegen, Variablen binden und Objekte nach Typ unterscheiden – alles in einer kompakten, gut lesbaren Syntax. Denk daran, dass das Feature erst ab Python 3.10 verfügbar ist. Probiere es beim nächsten Mal aus, wenn du eine unübersichtliche if/elif-Kette vor dir hast – oft lässt sie sich damit deutlich eleganter schreiben.