Skip to Content
Course content

60: The match Statement for Pattern Matching

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Is this just a "switch" statement from other languages?

That's the first thing everyone asks, and the answer is: not exactly. If you're coming from C++ or Java, you're probably thinking of a simple value check. While match can do that, it's actually called "Structural Pattern Matching." The "structural" part is key. It doesn't just check if x == y; it checks if the shape of the data matches a specific pattern.

I usually use it when I'm dealing with commands or messages. Instead of a giant chain of if isinstance(...) and elif value == ..., you can describe what the data should look like. Check this out:

def handle_command(command):
    match command.split():
        case ["quit"]:
            print("Shutting down...")
        case ["load", filename]:
            print(f"Loading file: {filename}")
        case ["move", direction]:
            print(f"Moving the character {direction}")
        case _:
            print("I don't understand that command.")

Notice how in the ["load", filename] case, Python isn't just checking if the list has two elements; it's actually extracting the second element and assigning it to the variable filename for you. That's a huge time-saver.

How do I handle more complex patterns, like dictionaries or objects?

This is where match really starts to outshine the old if/elif approach. You can match against the structure of a dictionary or even a custom class. You don't have to manually check for keys; you just define the keys you care about.

Imagine you're processing a JSON response from an API. You might only care about certain fields depending on the "type" of the response:

def process_event(event):
    match event:
        case {"type": "click", "x": x, "y": y}:
            print(f"Mouse clicked at {x}, {y}")
        case {"type": "keypress", "key": key}:
            print(f"Key pressed: {key}")
        case {"type": "scroll", "direction": dir}:
            print(f"Scrolled {dir}")
        case _:
            print("Unknown event type")

I love this because it's declarative. You're telling Python, "If the dictionary has a key 'type' with the value 'click', and it also has 'x' and 'y', then do this." If the dictionary has extra keys you didn't mention, Python doesn't care—it still matches. It only cares that the requirements you specified are met.

Can I add extra logic to a case without writing a whole new block?

Yes, and this is a feature called "guards." Sometimes a pattern match isn't enough. You might know the shape of the data is correct, but you need to verify a specific value before proceeding. You do this by adding an if statement directly to the case line.

Here is a practical example. Let's say you're building a simple permission system. You want to match a user's role, but you only want to allow a "manager" to delete a record if the record is marked as "archived":

def delete_record(user, record):
    match (user["role"], record["status"]):
        case ("admin", _):
            print("Admin delete: Allowed")
        case ("manager", "archived"):
            print("Manager delete: Allowed (Archived record)")
        case ("manager", status) if status != "archived":
            print(f"Manager delete: Denied. Record is {status}")
        case _:
            print("Delete: Denied")

That if status != "archived" is the guard. If the pattern matches (it's a manager and there is a status), but the guard evaluates to False, Python just skips that case and moves to the next one. It keeps your logic flat and readable instead of nesting if statements inside your case blocks.

What is the deal with the underscore symbol?

You've probably noticed the case _: at the end of my examples. In pattern matching, the underscore is the "wildcard." It matches anything. Since match evaluates cases from top to bottom, the wildcard acts as your else or default case.

One thing to keep in mind: if you use a variable name instead of an underscore (like case other:), Python actually binds the value to that variable. Using _ tells Python, "I know something is here, but I don't actually care what it is, so don't bother saving it to a variable." It's a small distinction, but it's cleaner and signals your intent to other developers.




📋 Practical Task

Building a Smart API Response Parser

You are building a system that processes responses from a weather API. The API returns a list containing a status code and a data payload. Depending on the status and the content of the payload, your program needs to respond differently.

Your Task: Write a function parse_weather_response(response) using a match statement that handles the following scenarios:

  • If the response is [200, {"temp": temperature, "unit": "C"}], print "The temperature is {temperature} degrees Celsius."
  • If the response is [200, {"temp": temperature, "unit": "F"}], print "The temperature is {temperature} degrees Fahrenheit."
  • If the response is [404, "City not found"], print "Error: The requested city was not found."
  • If the response is [500, _] (any data), print "Error: Server-side issue occurred."
  • For any other response shape, print "Error: Received an unexpected response format."

Test your function with these inputs:

print(parse_weather_response([200, {"temp": 22, "unit": "C"}]))
print(parse_weather_response([200, {"temp": 72, "unit": "F"}]))
print(parse_weather_response([404, "City not found"]))
print(parse_weather_response([500, "Database connection timeout"]))
print(parse_weather_response([403, "Forbidden"]))
Rating
0 0

There are no comments for now.

to be the first to leave a comment.