Skip to content
Foxipa

Suggest an edit

Tell us what should be corrected, clarified, or improved. Submission will be connected to Foxipa’s contribution service later.

No information is sent anywhere yet. Your suggestion is only prepared in your browser.

Learning

Python Structural Pattern Matching

Learn Python match statements through literal, capture, sequence, mapping, class, value, wildcard, guard, OR, and AS patterns.

Levels: Intermediate ~27 min read

Intermediate

Python pattern matching lets you compare one subject against a series of patterns and run the first matching case. Patterns can also inspect the structure of sequences and mappings and bind matched values to names.

The basic match statement

A match statement evaluates its subject, then tries its case patterns in order. The first case whose pattern succeeds is selected. If no case matches, nothing runs unless you provide a final wildcard case.

python
status = 404

match status:
    case 200:
        message = "OK"
    case 404:
        message = "Not found"
    case _:
        message = "Other status"

print(message)

Literal patterns and the wildcard

A literal pattern can match a value such as an integer or string. The underscore pattern, _, is a wildcard: it matches anything and does not bind a value to a name. Put a match-all case last because it cannot fail.

python
command = "stop"

match command:
    case "start":
        result = "Starting"
    case "stop":
        result = "Stopping"
    case _:
        result = "Unknown command"

print(result)

Capture values from a sequence

Sequence patterns can match the shape of a sequence while capturing its elements into names. A starred name can capture the remaining items, similar to extended unpacking.

python
point = (4, 7)

match point:
    case (x, y):
        print(f"x={x}, y={y}")
    case _:
        print("Not a pair")

Match mapping data

Mapping patterns match selected keys and can bind their values. Extra keys in the mapping do not prevent a mapping pattern from matching, so the pattern can focus on the fields it needs.

python
request = {"method": "GET", "path": "/home", "secure": True}

match request:
    case {"method": "GET", "path": path}:
        print(f"Read {path}")
    case {"method": "POST", "path": path}:
        print(f"Write {path}")
    case _:
        print("Other request")

Use a guard for an extra condition

A case can include a guard after its pattern. The pattern must succeed first; only then is the guard expression evaluated. If the guard is false, Python continues to the next case.

python
number = 12

match number:
    case n if n < 0:
        label = "negative"
    case n if n % 2 == 0:
        label = "non-negative even"
    case _:
        label = "other"

print(label)

Match alternatives with |

An OR pattern uses | to accept more than one pattern. Every alternative must bind the same set of names. This is useful when several shapes should lead to the same branch.

python
value = "yes"

match value:
    case "yes" | "y":
        print("accepted")
    case "no" | "n":
        print("rejected")
    case _:
        print("unknown")

A bare name captures; a dotted name checks a value

A bare name such as READY is a capture pattern, even when it is written in uppercase. It succeeds for any subject and binds that subject to the name; it does not compare against a variable or constant that was defined earlier. To match a named value, use a dotted name such as State.READY or Color.RED. This distinction prevents a subtle bug: an unguarded capture pattern is irrefutable, so it can also make later case clauses unreachable.

Interactive Runnable example
Edit

Ready to run.

Capture or compare?

Which pattern compares value with the existing State.READY value instead of binding a new name?

class State:
    READY = "ready"
READY = State.READY
value = "stopped"

match value:
    case :
        print("matched")
print(READY, locals().get("state", "not bound"))

Match object attributes with a class pattern

A class pattern checks whether the subject is an instance of the named class and then matches the requested attributes. In Point(x=x, y=y), Point is the class, the left-hand x and y are attribute names, and the names on the right capture their values. You can add an AS pattern to keep the whole matched object as well: Point(x=x, y=y) as matched_point binds both coordinates and the original subject. Matching inspects the existing object; it does not create a new Point.

Interactive Runnable example
Edit

Ready to run.

Recognize a class pattern

What does Point(x=x, y=y) test and bind when the subject is point?

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
class Circle:
    def __init__(self, x, y):
        self.x = x
        self.y = y
point = Point(4, 7)
match point:
    case :
        print(locals().get("x", "not captured"), locals().get("y", "not captured"))
    case _:
        print("no match")

When match is a good fit

Use match when the decision naturally depends on the structure or shape of one subject. A simple if/elif chain can be clearer when you are only testing unrelated Boolean conditions. Pattern matching is not merely a switch statement: sequence and mapping patterns can inspect structure and bind values.

python
event = ["click", 120, 80]

match event:
    case ["click", x, y]:
        print(f"Clicked at {x}, {y}")
    case ["keypress", key]:
        print(f"Pressed {key}")
    case _:
        print("Other event")

Key takeaways

  • match evaluates one subject against case patterns in order and selects the first successful case.
  • _ is a wildcard pattern that matches anything and does not bind a value.
  • Sequence and mapping patterns can inspect structure while binding matched values to names.
  • A guard is an if condition checked after its case pattern succeeds.
  • Use match when structural patterns make the decision clearer; use ordinary conditionals when they are simpler.
Was this page helpful?

Mark this learning resource complete to track your learning progress.