How to use match case in Python
Learn how to use Python's match-case statement. Explore different methods, tips, real-world examples, and how to debug common errors.

Python's match-case statement provides a powerful alternative to complex if-elif-else blocks. This feature simplifies your code's structure and makes your conditional logic much easier to read.
Here, you'll discover key techniques and practical tips for effective implementation. You'll explore real-world applications and receive expert advice to debug your match-case statements for cleaner, more efficient code.
Basic usage of match case
day = "Monday"
match day:
case "Monday":
print("Start of the work week")
case "Friday":
print("End of the work week")
case _:
print("Some other day")--OUTPUT--Start of the work week
In this example, the match statement evaluates the day variable. It then compares its value against each case pattern sequentially. When it finds a match, such as "Monday", it executes the associated code and immediately exits the block.
The most important part here is the wildcard pattern, case _. This acts as a catch-all, similar to an else clause. It ensures that if no other specific cases match, your code still has a defined path to follow, preventing unexpected behavior.
Pattern matching fundamentals
Beyond simple value checks, match-case lets you handle multiple values in one case, pull out specific data, and add if conditions for more complex logic.
Matching multiple values with case
day = "Saturday"
match day:
case "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday":
print("Weekday")
case "Saturday" | "Sunday":
print("Weekend")--OUTPUT--Weekend
To handle multiple values in one go, you can use the | operator within a single case statement. This operator functions like a logical "or", making your code more compact and readable than long if-elif chains.
- In the example,
case "Saturday" | "Sunday"checks if thedayvariable matches either value. - This technique is perfect for grouping related conditions without writing repetitive code.
Extracting values with pattern matching
command = ("move", 10, 20)
match command:
case ("move", x, y):
print(f"Moving to position ({x}, {y})")
case ("quit",):
print("Quitting the program")
case _:
print("Unknown command")--OUTPUT--Moving to position (10, 20)
Pattern matching shines when you need to pull values out of structured data. Instead of just checking for equality, you can match the shape of an object, such as a tuple, and capture its contents into variables.
- The pattern
("move", x, y)checks if the tuple starts with"move"and has two following items. - If it matches, the values
10and20are automatically bound to the variablesxandy. - This lets you use the extracted data right away, making your code cleaner than manual indexing.
Using wildcards and if guards
point = (10, 20)
match point:
case (x, y) if x == y:
print(f"Point ({x}, {y}) is on the diagonal")
case (0, y):
print(f"Point is on the y-axis at y={y}")
case (x, 0):
print(f"Point is on the x-axis at x={x}")
case (x, y):
print(f"Point is at coordinates ({x}, {y})")--OUTPUT--Point is at coordinates (10, 20)
You can make your patterns more powerful by adding if guards for extra logic. The line case (x, y) if x == y: shows this in action—it only matches if the tuple's values are equal. This lets you add custom logic right inside the case.
- The order of your
casestatements is critical. Specific patterns likecase (x, 0):must come before a general one likecase (x, y):to work correctly. - The final
case (x, y):acts as a fallback, capturing any two-item tuple that didn't meet the more specific criteria above it.
Advanced pattern matching techniques
Building on the fundamentals, you can now deconstruct complex sequences and classes, and even use the := operator for more dynamic and powerful pattern matching.
Matching with sequences and unpacking
values = [1, 2, 3, 4, 5]
match values:
case [first, second, *rest]:
print(f"First: {first}, Second: {second}, Rest: {rest}")
case [single]:
print(f"Only one item: {single}")
case []:
print("Empty list")--OUTPUT--First: 1, Second: 2, Rest: [3, 4, 5]
You can use match-case to unpack sequences like lists, which is especially useful for handling variable-length data. The star operator, *, lets you capture multiple items at once.
- The pattern
[first, second, *rest]matches a list with at least two elements. It assigns the first two items tofirstandsecond, while*restcollects the remaining elements into a new list. - You can also match specific list lengths, such as
[single]for a one-item list or[]for an empty one.
Matching with mappings and class patterns
data = {"name": "Alice", "age": 30}
match data:
case {"name": str(name), "age": int(age)} if age >= 18:
print(f"{name} is an adult")
case {"name": str(name)}:
print(f"{name}'s age is unknown or they're a minor")
case _:
print("Unknown data format")--OUTPUT--Alice is an adult
You can also use match-case to unpack dictionaries and other mappings. This lets you check for specific keys, validate their value types, and capture the data all in one step.
- The pattern
{"name": str(name), "age": int(age)}verifies that the dictionary contains both keys and that their values are a string and an integer, respectively. - You can attach an
ifguard, likeif age >= 18, to add conditional logic directly to your case.
This approach is much cleaner than nested if statements for checking dictionary structures.
Using the := walrus operator in pattern matching
command = "SUM 10 20 30"
match command.split():
case ["SUM", *values] if all(v.isdigit() for v in values) and (nums := [int(v) for v in values]):
print(f"Sum of {nums} is {sum(nums)}")
case ["AVERAGE", *values] if (nums := [float(v) for v in values if v.replace('.', '', 1).isdigit()]):
print(f"Average of {nums} is {sum(nums)/len(nums)}")--OUTPUT--Sum of [10, 20, 30] is 60
The walrus operator, :=, lets you assign a value to a variable as part of a larger expression, making your code more compact. It’s especially useful inside an if guard to capture a value that you need to both test and use.
- In the
SUMcase, the expression(nums := [int(v) for v in values])converts the captured string values into a list of integers and assigns it tonumsall at once. - This allows the
ifguard to check the condition while simultaneously making thenumsvariable available for theprint()function, avoiding repetitive code.
Move faster with Replit
Replit is an AI-powered development platform that lets you start coding Python instantly. It comes with all dependencies pre-installed, so you can skip the tedious setup and get straight to writing code.
While mastering techniques like match-case is powerful, Agent 4 helps you move from learning individual concepts to building complete applications. It takes your idea and builds a working product by handling the code, databases, APIs, and deployment directly from your description.
Instead of piecing together functions, you can describe the app you want to build. For example, you could create:
- A command-line tool that parses user inputs like
'MOVE 10 20'and executes the corresponding action. - A data validator that checks incoming JSON objects against a required schema, ensuring fields like
'name'and'age'are present and correctly typed. - A log processor that categorizes system events by matching different log formats, such as errors, warnings, and info messages.
Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.
Common errors and challenges
While match-case is powerful, a few common pitfalls can lead to unexpected bugs if you're not careful.
Forgetting the case _ wildcard for handling unexpected values
Leaving out the case _ wildcard is a frequent mistake. Without this catch-all, if no other pattern matches the subject, your code fails silently without raising an error, which can make debugging tricky. Always include a case _ to handle unexpected values and prevent your program from doing nothing when you expect it to do something.
Incorrectly ordering case patterns from general to specific
The order of your case statements matters because Python stops at the first match. If you place a general pattern like case (x, y): before a more specific one like case (0, y):, the specific case will never be reached. Always structure your patterns from most specific to most general to ensure your logic works as intended.
Failing to validate types in pattern matching
Pattern matching can capture values without checking their types, which can lead to errors later on. For example, a pattern might capture an age as a string when you expect an integer, causing a TypeError during a comparison. You can prevent this by including type checks directly in your patterns, such as case {"age": int(age)}:, to ensure data integrity from the start.
Forgetting the case _ wildcard for handling unexpected values
A common pitfall is omitting the case _ wildcard. Without this catch-all, your match statement won't raise an error if no pattern matches; it just finishes silently. See how this plays out in the code below where an unexpected value appears.
status_code = 500 # Server error
match status_code:
case 200:
print("Success")
case 301:
print("Permanent redirect")
case 404:
print("Not found")
The status_code of 500 doesn't match any case, so the block executes nothing. This silent failure can be tricky to spot. The following example shows how to properly handle these unexpected values.
status_code = 500 # Server error
match status_code:
case 200:
print("Success")
case 301:
print("Permanent redirect")
case 404:
print("Not found")
case _:
print(f"Unhandled status code: {status_code}")
By adding case _:, the code now gracefully handles any status_code that doesn't match the specific cases. This wildcard pattern acts as a safety net, catching unexpected values like 500 and printing a helpful message. It's a crucial practice that prevents silent failures, making your code more robust and easier to debug. Always include it when you can't guarantee every possible input value is covered by a specific case.
Incorrectly ordering case patterns from general to specific
The order of your case statements is crucial because Python executes the first one that matches and then stops. If a general pattern like case [*items]: comes before a more specific one, the specific case will never run. See what happens below.
value = [1, 2, 3]
match value:
case [*items]:
print(f"List with {len(items)} items")
case [1, 2, 3]:
print("Exact match: [1, 2, 3]")
case [1, *rest]:
print(f"List starting with 1, rest: {rest}")
The first pattern, [*items]:, is too general and matches any list. This prevents the more specific patterns below it from ever being checked. See how reordering the cases fixes the logic in the code below.
value = [1, 2, 3]
match value:
case [1, 2, 3]:
print("Exact match: [1, 2, 3]")
case [1, *rest]:
print(f"List starting with 1, rest: {rest}")
case [*items]:
print(f"List with {len(items)} items")
By placing the most specific pattern, case [1, 2, 3]:, at the top, you ensure it gets checked first. More general patterns, like case [1, *rest]: and case [*items]:, now follow. This structure guarantees that logic for exact matches isn't overshadowed by a broader catch-all.
Always order your case statements from most specific to least specific to prevent logical errors, especially when matching sequences of different lengths or structures.
Failing to validate types in pattern matching
Pattern matching captures values, but it won't check their types, which can lead to subtle bugs. You might expect to add numbers but end up concatenating strings instead. The code below shows how this can go wrong in practice.
data = {"id": "1234", "count": "42"}
match data:
case {"id": id, "count": count}:
result = id + count # Will fail if expecting numbers
print(f"Total: {result}")
Because id and count are captured as strings, the + operator concatenates them into "123442" instead of adding them. This leads to an unexpected logical error. See how to prevent this in the next example.
data = {"id": "1234", "count": "42"}
match data:
case {"id": str(id_str), "count": str(count_str)}:
result = int(id_str) + int(count_str)
print(f"Total: {result}")
This solution works by explicitly validating types. The pattern {"id": str(id_str), "count": str(count_str)} confirms the values are strings before capturing them. You must then manually convert them with int() before performing arithmetic. This is crucial when processing data from external sources like APIs or user input—where you can't guarantee data types—to prevent unexpected logical errors.
Real-world applications
By avoiding common pitfalls, you can confidently use match-case for practical tasks like processing API responses and building command-line interfaces.
Processing API responses with match case
You can use match-case to cleanly parse API responses, easily handling different structures for success, error, or other statuses.
api_response = {"status": "success", "data": {"users": ["Alice", "Bob"]}}
match api_response:
case {"status": "success", "data": data}:
print(f"Successfully retrieved data: {data}")
case {"status": "error", "message": msg}:
print(f"Error occurred: {msg}")
case {"status": status}:
print(f"Unknown status: {status}")
case _:
print("Invalid response format")
This example shows how match-case effectively deconstructs a dictionary, which is ideal for processing API responses that can have different formats. The code checks the api_response against several patterns to find a match.
- It first looks for a successful response with
{"status": "success"}and captures the nesteddata. - If that fails, it checks for an error structure with
{"status": "error"}to extract themessage. - The final
case _is a crucial fallback, catching any format that doesn't fit the expected patterns and preventing silent failures.
Building a command-line interface parser with match case
The match-case statement is also ideal for parsing command-line input, allowing you to deconstruct commands and their arguments from a list of strings.
command = ["git", "commit", "-m", "Initial commit"]
match command:
case ["git", "push", *args]:
print(f"Pushing to remote repository with args: {args}")
case ["git", "commit", "-m", message]:
print(f"Committing changes with message: {message}")
case ["git", "status"]:
print("Checking repository status")
case ["git", cmd, *_]:
print(f"Git command not implemented: {cmd}")
case [cmd, *args]:
print(f"Unknown command: {cmd} with args: {args}")
This example demonstrates how match-case excels at parsing structured lists. The statement evaluates the command list, trying each case pattern in sequence until it finds a match. The order is critical—specific patterns must come before general ones.
- The pattern
["git", "commit", "-m", message]matches the input and captures the commit text into themessagevariable. - The
*argssyntax gathers all remaining list items, while*_discards them. - Broader cases at the end catch unimplemented or unknown commands, preventing errors.
Get started with Replit
Now, turn your knowledge of match-case into a real tool. Tell Replit Agent: "Build a CLI calculator that parses commands" or "Create a script to process API responses based on their status key."
Replit Agent writes the code, tests for errors, and deploys your application directly from your instructions. Start building with Replit.
Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.
Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.

.png)
.png)
.png)