# Python match (Pattern Matching): How Structural Pattern Matching Works

Source: https://devdocs10.com/articles/python/python-match  
By Murat Bo · Published 2026-11-13 · DevDocs10 (https://devdocs10.com)

`match` lets Python choose one branch from many by comparing a value against patterns, not just by testing one boolean condition at a time. It is useful when you need to inspect the shape of data, such as lists, tuples, dictionaries, or custom objects, and handle each shape clearly. This article shows how Python pattern matching works, how to write common patterns, what usually goes wrong, and when to use it instead of ordinary `if` statements.

> **Difficulty:** Intermediate
>
> **Helpful to know first:** basic Python syntax, `if`/`elif`/`else`, functions, and common data types like lists, tuples, and dictionaries.

## How Python match Chooses One Case From Many

The `match` statement compares one subject value against a series of `case` patterns. The first pattern that fits wins, and Python runs only that block. If no pattern fits, Python skips to `case _` when you provide one, or it does nothing if you do not.

This is different from a long chain of `if` statements. In a match block, you describe the shape or contents you expect, and Python does the comparison for you.

```python
command = "start"

match command:
    case "start":
        print("Starting")
    case "stop":
        print("Stopping")
    case _:
        print("Unknown command")
# Output: Starting
```

In this example, `command` is the subject. Python checks each `case` from top to bottom. The first pattern that matches the string `"start"` prints `Starting`.

> The underscore pattern `case _` is a catch-all. It matches anything, so it belongs last unless you want to stop all other cases from running.

## What You Can Match in Python 3.12

Python pattern matching is built for structure. You can match simple literals, capture values into names, match sequences like lists and tuples, match mappings like dictionaries, and match objects by their attributes. You can also combine patterns with `|` and add extra conditions with a guard.

Here is the smallest useful mental model:

- `case 10` means “match the value 10.”
- `case name` means “capture the value in a new variable named `name`.”
- `case [x, y]` means “match a two-item sequence and unpack it.”
- `case {"key": value}` means “match a dictionary with this key.”
- `case _` means “match anything.”

The biggest surprise for beginners is that names inside a pattern usually capture values. They are not compared to existing variables unless the pattern syntax makes that literal comparison unambiguous.

## Matching Simple Values and Capturing Names

Literal patterns are the easiest way to use `match`. They are useful when the value is one of a few exact options. Capture patterns are useful when you want the actual value, not just a yes or no answer.

### Matching exact literals

This version is similar to comparing against a menu of accepted strings or numbers. It is a good fit for commands, status codes, and small enumerations.

```python
status = 404

match status:
    case 200:
        print("OK")
    case 404:
        print("Not found")
    case 500:
        print("Server error")
    case _:
        print("Other status")
# Output: Not found
```

This code shows that a literal pattern only matches the same exact value. Since `status` is `404`, Python jumps to the `case 404` branch.

### Capturing part of a result

Capture patterns are common when one case should handle many values, but still needs the matched value later. The captured name becomes a new local variable inside that case block.

```python
point = 7

match point:
    case n:
        print(f"Got number: {n}")
# Output: Got number: 7
```

Because `case n` is a capture pattern, it matches anything and stores the subject in `n`. This is a common source of confusion: `n` is not being compared to an existing variable named `n` here.

## Matching Lists and Tuples by Shape

Sequence patterns are one of the main reasons to use `match`. They let you describe the length and layout of a list or tuple directly. This is cleaner than indexing into the sequence and checking its length in separate steps.

### Matching exact sequence lengths

When you know the data shape, you can write the shape into the pattern. Python checks both the number of items and their positions.

```python
coords = [3, 4]

match coords:
    case [x, y]:
        print(f"x={x}, y={y}")
    case [x]:
        print(f"One item: {x}")
    case _:
        print("Unexpected shape")
# Output: x=3, y=4
```

The first case matches because `coords` has exactly two items. Python assigns the first item to `x` and the second item to `y`.

### Using a star pattern for “the rest”

If you care about the first few items and want to ignore the rest, use a starred name. This is useful for logs, command arguments, and records with extra fields.

```python
data = ["error", 500, "retry later"]

match data:
    case ["error", code, *details]:
        print(code)
        print(details)
# Output: 500
# Output: ['retry later']
```

The starred name collects the remaining items as a list. Here, `details` becomes a list containing one string.

## Matching Dictionaries by Key

Mapping patterns let you match dictionaries by required keys. This is useful for decoded JSON, request payloads, configuration data, and other nested records.

```python
user = {"name": "Ada", "role": "admin"}

match user:
    case {"name": name, "role": "admin"}:
        print(f"Admin user: {name}")
    case {"name": name}:
        print(f"Regular user: {name}")
    case _:
        print("Unknown record")
# Output: Admin user: Ada
```

Only the keys named in the pattern are required. Extra keys are allowed. That means `{"name": "Ada", "role": "admin", "id": 7}` would still match the first case.

> Dictionary patterns do not care about key order. They care about whether the required keys are present and whether the matched values fit the pattern.

## Matching Multiple Options and Adding Guards

Pattern matching becomes more useful when you combine patterns. The `|` operator means “either of these patterns.” A guard adds a normal boolean condition that must also be true.

### Using one case for several values

When several inputs should lead to the same action, an OR pattern keeps the code short and clear.

```python
day = "Sat"

match day:
    case "Sat" | "Sun":
        print("Weekend")
    case _:
        print("Weekday")
# Output: Weekend
```

The subject only needs to match one side of the pattern. Here, `day` matches `"Sat"`, so the first branch runs.

### Adding a guard after a successful pattern

A guard filters matches further. The pattern must match first, and then the guard expression must evaluate to true.

```python
score = 82

match score:
    case n if n >= 90:
        print("A")
    case n if n >= 80:
        print("B")
    case _:
        print("Lower grade")
# Output: B
```

Here, `score` matches the second case because the value is at least 80. Guards are useful when the pattern alone is not specific enough.

## How Class Patterns Work With Objects

Class patterns let you match objects by type and, optionally, by their attributes. This is powerful when working with dataclasses, simple model objects, and parsed records.

```python
from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

p = Point(2, 5)

match p:
    case Point(x, y):
        print(f"Point at {x}, {y}")
    case _:
        print("Not a Point")
# Output: Point at 2, 5
```

Python uses the class name in the pattern and then checks the object’s shape. For dataclasses, the generated field order makes this especially convenient. You can also match named attributes with keyword patterns when that reads better.

> Class patterns are most useful when you control the object type. For loosely shaped data from outside your code, mapping patterns on dictionaries are often simpler and more explicit.

## How match Behaves Differently From if and elif

The biggest practical difference is that `match` focuses on the shape of one value, while `if` focuses on conditions that can be any boolean expression. A `match` block is usually easier to read when you are branching on the same value in several different forms.

For example, compare these two versions:

```python
command = "stop"

if command == "start":
    print("Starting")
elif command == "stop":
    print("Stopping")
else:
    print("Unknown command")

# Output: Stopping
```

The `if` version works well, but it repeats the same variable and comparison style several times. The `match` version groups the possible shapes and values in one place, which can be easier to maintain when the cases get more complex.

### When match is clearer than if

Use `match` when one value can naturally be described by several patterns: a command string, a tuple structure, a dictionary payload, or a union of several accepted forms. Use `if` when the logic is mostly about unrelated conditions, such as combining several different variables or checking ranges and flags in an arbitrary order.

| Situation | Better choice | Why |
| --- | --- | --- |
| One value, many possible shapes | `match` | Patterns describe the data directly |
| Several unrelated conditions | `if` | Boolean expressions are more natural |
| Need to unpack list or dict structure | `match` | Can bind parts of the value in one step |
| Simple two-way branch | `if` | Shorter and easier to read |

## Predict the Output

### Capture pattern or literal comparison?

What does this print?

```python
name = "Ada"

match name:
    case name:
        print("same")
    case _:
        print("different")
```

**Answer:**

It prints `same`.

The name in `case name` is a capture pattern, so it matches anything and binds the subject to a new local name. The first case always succeeds, so Python never reaches `case _`.

### Sequence length and starred binding

What happens?

```python
items = [1, 2, 3]

match items:
    case [first, second]:
        print("two items")
    case [first, *rest]:
        print(first, rest)
```

**Answer:**

It prints `1 [2, 3]`.

The first case fails because the list has three items, not two. The second case matches the first element to `first` and collects the remaining items into `rest` as a list.

## Common Mistakes With Python match Patterns

### Thinking an unqualified name is a literal value

Beginners often expect `case color` to mean “match the value stored in `color`.” That is not how capture patterns work.

**Problem:** This case matches every subject, because `color` is treated as a new capture name instead of a value to compare against.

```python
color = "red"
value = "blue"

match value:
    case color:
        print("matched")
```

**Fix:** Compare against a literal, or use a guard if you need to compare with an existing variable.

```python
color = "red"
value = "blue"

match value:
    case "red":
        print("matched")
    case other if other == color:
        print("matched")
```

The corrected version works because the comparison is now explicit, so Python only matches when the value really equals the existing variable.

### Putting a broader case before a narrower one

`match` stops at the first successful case. If a broad pattern appears first, later more specific cases may never run.

**Problem:** The first case `case [first, *rest]` matches every non-empty list, so the exact two-item case below it is unreachable.

```python
items = [1, 2]

match items:
    case [first, *rest]:
        print("at least one item")
    case [first, second]:
        print("exactly two items")
```

**Fix:** Put the most specific patterns first and the broad catch-all patterns last.

```python
items = [1, 2]

match items:
    case [first, second]:
        print("exactly two items")
    case [first, *rest]:
        print("at least one item")
```

The corrected version works because the exact-length case gets a chance to match before the broader pattern claims the value.

### Forgetting that a guard is not part of the pattern

A guard runs after the pattern has already matched. If you place an important check only in the guard, the pattern can still bind values even when the guard fails.

**Problem:** This code assumes the first case will handle only positive numbers, but the guard can fail after `n` is already bound.

```python
n = -3

match n:
    case x if x > 0:
        print("positive")
    case _:
        print("not positive")
```

**Fix:** Keep the guard if you need it, but remember that the pattern still matches the shape before the guard decides whether the case runs.

```python
n = -3

match n:
    case x if x > 0:
        print("positive")
    case _:
        print("not positive")
```

The corrected version behaves the same at runtime, but the important lesson is that the pattern and the guard do different jobs.

## Find the Bug

Find the subtle problem in this `match` block. It looks like it should handle two-item coordinate pairs and longer coordinate lists, but one case is in the wrong place.

```python
coords = [10, 20]

match coords:
    case [x, *rest]:
        print("long list")
    case [x, y]:
        print(f"pair: {x}, {y}")
```

**Answer:**

The first pattern is too broad. `case [x, *rest]` matches any non-empty list, including a two-item list, so the second case can never run.

**Fixed code:**

```python
coords = [10, 20]

match coords:
    case [x, y]:
        print(f"pair: {x}, {y}")
    case [x, *rest]:
        print("long list")
```

The fixed version works because the exact pair case gets checked before the broader sequence pattern.

## When to Use match and When to Use Something Else

Use `match` when one subject value can be described by several structural patterns, especially when you need to unpack parts of that value. Use `if` when your decision depends on several unrelated conditions or when the test is just a plain boolean expression.

| Use `match` when... | Use `if` when... |
| --- | --- |
| You are branching on one value with several shapes | You are combining many boolean checks |
| You want to unpack items while matching | You only need a simple yes/no decision |
| You are matching dictionaries, sequences, or objects | You are checking ranges, flags, or mixed conditions |
| Clarity improves when cases read like data shapes | Clarity improves when the logic reads like a normal condition |

> A common rule of thumb: if you can describe each branch as a pattern against the same subject, `match` is a good candidate. If the branches are just different boolean tests, `if` is usually simpler.

### Interview questions about Python pattern matching

### What problem does match solve?

`match` helps you choose branches based on the structure of a value, not just its truthiness or one simple comparison. It is especially helpful for lists, tuples, dictionaries, and custom objects.

### Why does case name match everything?

Because `name` is a capture pattern. It binds whatever value is being matched to a new local variable. If you want to compare to an existing variable, use a literal or a guard.

### Why can only one case run?

Python stops at the first successful match. That makes ordering important, because broad patterns can block later specific patterns.

## Try It Yourself

Write a `match` block that classifies a point stored as a two-item list:

- If the list is `[0, 0]`, print `origin`.
- If the list is `[0, y]`, print `y-axis` and the value of `y`.
- If the list is `[x, 0]`, print `x-axis` and the value of `x`.
- For any other two-item list, print `other point` and both values.

**Expected output:**

```python
y-axis 5
```

**Hint:** Put the most specific pattern first. Use a guard only when the structure alone is not enough.

**Solution:**

```python
point = [0, 5]

match point:
    case [0, 0]:
        print("origin")
    case [0, y]:
        print("y-axis", y)
    case [x, 0]:
        print("x-axis", x)
    case [x, y]:
        print("other point", x, y)
```

## Summary

- `match` chooses one branch by comparing a subject against patterns.
- Capture patterns bind names; they do not compare against existing variables.
- Sequence, mapping, and class patterns let you match the shape of data directly.
- Order matters because Python stops at the first successful case.
- Guards add extra boolean checks after the pattern matches.
- Use `match` when the subject has a clear structure; use `if` for plain boolean logic.

Next, read about `Python if statements` and `Python dictionaries`, since both help you compare when `match` is the clearest tool.
