W3docs

Python match-Anweisung

Lerne strukturelles Pattern Matching in Python mit match/case: Literale, Sequenzen, Mappings, Klassen, Guards und den Wildcard – mit Beispielen.

Python 3.10 führte strukturelles Pattern Matching mit der match-Anweisung ein — eine leistungsstarke Möglichkeit, auf die Form und den Inhalt von Daten zu verzweigen, nicht nur auf Gleichheit. Dieses Kapitel behandelt alles von der grundlegenden match/case-Syntax bis zu fortgeschrittenen Mustern wie Sequenz-Dekonstruktion, Mapping-Muster, Klassen-Muster, Guards und realen Anwendungsfällen.

Bevor du dieses Kapitel liest, solltest du mit Python if/else, Python-Funktionen und grundlegenden Datenstrukturen (Listen, Tupeln, Dictionaries) vertraut sein.

Was ist strukturelles Pattern Matching?

Strukturelles Pattern Matching ermöglicht es dir, die Struktur eines Objekts zu untersuchen — seinen Typ, die Werte seiner Felder, die Form einer Sequenz — und abhängig davon, welches Muster passt, unterschiedlichen Code auszuführen. Es geht weit über eine einfache if x == y-Prüfung hinaus.

Betrachten wir das Weiterleiten eines HTTP-Statuscodes. Mit if/elif-Ketten schreibt man:

if status == 200:
    print("OK")
elif status == 404:
    print("Not Found")
elif status == 500:
    print("Internal Server Error")
else:
    print("Unknown status")

Mit match ist die Absicht klarer:

match status:
    case 200:
        print("OK")
    case 404:
        print("Not Found")
    case 500:
        print("Internal Server Error")
    case _:
        print("Unknown status")

Der eigentliche Vorteil zeigt sich, wenn das Subjekt ein komplexes Objekt ist — ein Tupel, ein Dictionary oder eine Datenklasse — und du es beim Abgleichen dekonstruieren möchtest.

Grundlegende Syntax

match subject:
    case pattern1:
        # runs if subject matches pattern1
    case pattern2:
        # runs if subject matches pattern2
    case _:
        # wildcard — runs if nothing else matched

Zu merkende Regeln:

  • match und case sind weiche Schlüsselwörter — sie sind nur in diesem Kontext Schlüsselwörter und können an anderer Stelle im Code weiterhin als Variablennamen verwendet werden.
  • Jeder case-Block wird der Reihe nach geprüft; der erste Treffer gewinnt und die anderen werden übersprungen.
  • Der case _:-Block ist der Wildcard — er passt immer und dient als Standardauffangklausel.
  • Python 3.10+ ist erforderlich. Das Ausführen auf Python 3.9 oder früher löst einen SyntaxError aus.

Literal-Muster

Das einfachste Muster stimmt mit einem konkreten Wert überein: einer Zahl, einem string, True, False oder None.

def http_status(status):
    match status:
        case 200:
            return "OK"
        case 404:
            return "Not Found"
        case 500:
            return "Internal Server Error"
        case _:
            return "Unknown status"

print(http_status(200))   # OK
print(http_status(404))   # Not Found
print(http_status(999))   # Unknown status

ODER-Muster (|)

Verwende | innerhalb eines case, um mit einem von mehreren Literalen übereinzustimmen:

def is_vowel(letter):
    match letter.lower():
        case "a" | "e" | "i" | "o" | "u":
            return True
        case _:
            return False

print(is_vowel("a"))   # True
print(is_vowel("b"))   # False
print(is_vowel("E"))   # True

ODER-Muster funktionieren auch mit Zahlen, None und anderen Literal-Typen.

Capture-Muster

Ein Capture-Muster ist ein einfacher Name (kein string-Literal, kein Name mit Punkt), der mit allem übereinstimmt und den gematchten Wert an diesen Namen bindet, um ihn im Rumpf zu verwenden:

def greet(name):
    match name:
        case "Alice":
            return "Hello, Alice!"
        case other:          # captures whatever was passed
            return f"Hello, {other}!"

print(greet("Alice"))    # Hello, Alice!
print(greet("Bob"))      # Hello, Bob!

other oben ist ein Capture-Muster — es bindet den gematchten Wert an die lokale Variable other. Dies sieht dem Wildcard _ sehr ähnlich, aber _ verwirft den Wert, während ein benanntes Capture ihn beibehält.

Warnung

A bare name in a case is always a capture, never a comparison. If you want to compare against a constant defined elsewhere, use a dotted name like Status.OK or wrap it in a guard (case x if x == my_constant:).

Sequenz-Muster

Ein Sequenz-Muster stimmt mit Listen, Tupeln oder einer beliebigen Sequenz überein und kann die Elemente gleichzeitig in Variablen dekonstruieren.

def process_point(point):
    match point:
        case (0, 0):
            return "Origin"
        case (x, 0):
            return f"On x-axis at {x}"
        case (0, y):
            return f"On y-axis at {y}"
        case (x, y):
            return f"Point at ({x}, {y})"

print(process_point((0, 0)))   # Origin
print(process_point((5, 0)))   # On x-axis at 5
print(process_point((0, 3)))   # On y-axis at 3
print(process_point((2, 4)))   # Point at (2, 4)

* zum Erfassen des Rests verwenden

Ein *name innerhalb eines Sequenz-Musters sammelt verbleibende Elemente, ähnlich wie beim iterierbaren Entpacken:

def describe_list(items):
    match items:
        case []:
            return "empty list"
        case [single]:
            return f"one item: {single}"
        case [first, *rest]:
            return f"starts with {first!r}, then {len(rest)} more item(s)"

print(describe_list([]))              # empty list
print(describe_list([42]))            # one item: 42
print(describe_list([1, 2, 3, 4]))   # starts with 1, then 3 more item(s)

Verwende [first, *_], wenn du nur das erste Element erfassen und den Rest verwerfen möchtest.

Mapping-Muster

Ein Mapping-Muster stimmt mit Dictionaries (oder einem beliebigen Mapping) überein. Du gibst nur die Schlüssel an, die dich interessieren — zusätzliche Schlüssel im Subjekt werden ignoriert.

def process_event(event):
    match event:
        case {"type": "click", "button": button}:
            return f"Mouse click: button {button}"
        case {"type": "keypress", "key": key}:
            return f"Key pressed: {key!r}"
        case {"type": action}:
            return f"Other event: {action}"
        case _:
            return "Unknown event"

print(process_event({"type": "click", "button": 1}))
# Mouse click: button 1
print(process_event({"type": "keypress", "key": "Enter"}))
# Key pressed: 'Enter'
print(process_event({"type": "resize", "width": 800}))
# Other event: resize
print(process_event({}))
# Unknown event

Wichtiger Punkt: Ein Mapping-Muster schlägt niemals fehl wegen zusätzlicher Schlüssel im Subjekt. {"type": "click", "button": button} stimmt auch dann überein, wenn das Ereignis auch "x"- und "y"-Koordinaten enthält.

Um die verbleibenden Schlüssel/Wert-Paare zu erfassen, verwende **rest:

match event:
    case {"type": "click", **rest}:
        print(f"Click event with extra data: {rest}")

Klassen-Muster

Ein Klassen-Muster stimmt mit einer Instanz einer bestimmten Klasse überein und extrahiert deren Attribute. Dies ist besonders nützlich mit Datenklassen, da deren Attribute automatisch namentlich verfügbar gemacht werden.

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

@dataclass
class Circle:
    center: Point
    radius: float

def describe_shape(shape):
    match shape:
        case Point(x=0, y=0):
            return "Point at origin"
        case Point(x=x, y=y):
            return f"Point at ({x}, {y})"
        case Circle(center=Point(x=cx, y=cy), radius=r):
            return f"Circle centered at ({cx}, {cy}) with radius {r}"
        case _:
            return "Unknown shape"

print(describe_shape(Point(0, 0)))           # Point at origin
print(describe_shape(Point(3, 4)))           # Point at (3, 4)
print(describe_shape(Circle(Point(1, 2), 5)))# Circle centered at (1, 2) with radius 5

Beachte das verschachtelte Klassen-Muster im Circle-Fall: Point(x=cx, y=cy) wird innerhalb des Circle-Musters abgeglichen. Muster können beliebig tief zusammengesetzt werden.

Für eingebaute Typen wie int, str, float und bool kannst du positionale Muster mit einem einzelnen Argument verwenden:

def handle_input(value):
    match value:
        case (int() | float()) as number:
            return f"Got a number: {number}"
        case str() as text:
            return f"Got text: {text!r}"
        case _:
            return "Unknown type"

print(handle_input(3.14))    # Got a number: 3.14
print(handle_input("hello")) # Got text: 'hello'
print(handle_input([1, 2]))  # Unknown type

Das Schlüsselwort as (das AS-Muster) bindet den gesamten gematchten Wert an einen Namen, auch nach einer Typprüfung.

Guards

Ein Guard ist eine if-Bedingung, die nach einem Muster hinzugefügt wird. Der case passt nur, wenn das Muster passt und der Guard zu True ausgewertet wird.

def classify_number(n):
    match n:
        case 0:
            return "zero"
        case x if x < 0:
            return f"{x} is negative"
        case x if x % 2 == 0:
            return f"{x} is positive and even"
        case x:
            return f"{x} is positive and odd"

print(classify_number(0))    # zero
print(classify_number(-5))   # -5 is negative
print(classify_number(4))    # 4 is positive and even
print(classify_number(7))    # 7 is positive and odd

Guards werden ausgewertet, nachdem das strukturelle Muster übereinstimmt, sodass erfasste Variablen innerhalb von ihnen verfügbar sind. Ein fehlgeschlagener Guard verhindert nicht, dass spätere Fälle versucht werden.

Das Wildcard-Muster _

_ ist der universelle Auffangmechanismus. Er stimmt mit jedem Wert überein und bindet nichts (der Wert wird verworfen). Er sollte der letzte case in einem match-Block sein. Ohne ihn tut ein match, das keinen passenden case findet, einfach nichts — es wird kein Fehler ausgelöst.

def describe(value):
    match value:
        case 0:
            return "zero"
        case _:
            return f"something else: {value!r}"

print(describe(0))     # zero
print(describe(99))    # something else: 99
print(describe("hi"))  # something else: 'hi'

_ kann auch innerhalb eines Musters erscheinen, um bestimmte Teile zu ignorieren:

match point:
    case (_, 0):
        print("On the x-axis (x value doesn't matter)")
    case (0, _):
        print("On the y-axis (y value doesn't matter)")

Muster kombinieren: Ein praxisnahes Beispiel

Die oben genannten Muster lassen sich kombinieren. Hier ist ein Textabenteuer-Befehlsparser, der Sequenz-Muster, Guards und den Wildcard verbindet:

def run_command(command):
    match command.split():
        case ["quit"]:
            return "Quitting"
        case ["go", direction] if direction in ("north", "south", "east", "west"):
            return f"Going {direction}"
        case ["go", direction]:
            return f"Cannot go {direction!r} — try north, south, east, or west"
        case ["get", item]:
            return f"Picking up {item}"
        case ["drop", item]:
            return f"Dropping {item}"
        case ["inventory"]:
            return "Checking inventory"
        case [verb, *args]:
            return f"Unknown command {verb!r} with args {args}"
        case []:
            return "No command entered"

print(run_command("go north"))    # Going north
print(run_command("go up"))       # Cannot go 'up' — try north, south, east, or west
print(run_command("get sword"))   # Picking up sword
print(run_command("drop torch"))  # Dropping torch
print(run_command("quit"))        # Quitting
print(run_command(""))            # No command entered

Wenn man das von oben nach unten liest, versteht man sofort jeden unterstützten Befehl — etwas, das mit if/elif-Logik viele mehr Zeilen erfordern würde, um die gleiche Übersichtlichkeit zu erreichen.

match vs. if/elif — Wann was verwenden

SzenarioBeste Wahl
Einfache Gleichheit gegen wenige KonstantenBeides; match ist etwas übersichtlicher
Abgleich nach Datenstruktur / Formmatch — viel übersichtlicher
Werte beim Abgleichen dekonstruierenmatch — mit if nicht möglich
Logik, die nur berechnete Bedingungen beinhaltetif/elif
Python 3.9 oder früherif/elif (kein match verfügbar)
Eine Entscheidungstabelle klar ausdrückenmatch

match ist kein Ersatz für jede if-Kette. Wenn alle Zweige berechnete boolean-Bedingungen prüfen (z. B. if x > 10 and y < 5) ist eine if/elif-Kette natürlicher. match glänzt, wenn es bei der Form von Daten geht.

Häufige Fallstricke

Konstantennamen werden nicht nach Wert abgeglichen

Ein einfacher Name in einem case ist immer eine Capture, niemals eine Wert-Suche:

STATUS_OK = 200

match response_code:
    case STATUS_OK:          # WRONG — this captures into STATUS_OK, not compares!
        print("Success")

Um gegen eine benannte Konstante zu vergleichen, verwende einen Namen mit Punkt (http.HTTPStatus.OK) oder einen Guard:

match response_code:
    case x if x == STATUS_OK:
        print("Success")

match ist standardmäßig nicht erschöpfend

Anders als switch in einigen anderen Sprachen tut ein match ohne passenden case stillschweigend nichts. Füge ein case _: hinzu, wenn du einen garantierten Handler benötigst.

match erfordert Python 3.10+

Das Ausführen eines match-Blocks auf Python 3.9 oder früher löst SyntaxError: invalid syntax aus. Prüfe deine Version mit python3 --version. Siehe die Python-Einstiegsanleitung, wenn du eine moderne Python-Umgebung einrichten musst.

Muster sind keine boolean-Ausdrücke

Du kannst nicht case x > 5: schreiben — das ist ein Guard, kein Muster. Der strukturelle Teil (case x) muss zuerst kommen, gefolgt von einem optionalen if guard_expression.

Zusammenfassung

Muster-TypSyntax-BeispielWas es abgleicht
Literalcase 42:Exakter Wert
ODERcase "yes" | "y":Jede der Alternativen
Wildcardcase _:Alles (verwirft Wert)
Capturecase x:Alles, bindet an x
Sequenzcase [a, b, *rest]:Eine Sequenz mit mindestens 2 Elementen
Mappingcase {"key": val}:Ein dict, das die angegebenen Schlüssel enthält
Klassecase Point(x=0, y=y):Eine Instanz mit passenden Attributen
AScase int() as n:Stimmt überein und bindet den ganzen Wert
Guardcase x if x > 0:Muster + zusätzliche boolean-Bedingung

Übungen

Übung
Which Python version first introduced the match statement?
Which Python version first introduced the match statement?
Übung
In a match block, what does a bare variable name in a case clause do?
In a match block, what does a bare variable name in a case clause do?
Übung
Which pattern type would you use to match a dict that contains at least a 'type' key and extract its value?
Which pattern type would you use to match a dict that contains at least a 'type' key and extract its value?

Nachdem du nun weißt, wie man auf Datenstrukturen verzweigt, erkunde Python for-Schleifen, um über Sequenzen zu iterieren, oder Python enums, um typisierte Konstanten zu definieren, die gut mit Klassen-Mustern funktionieren.

Was this page helpful?