Python

P
PythonHub
Progress 0%

Python

  • Home
  • History of Python
  • Applications of Python
  • Introduction To Python
    • What is Python
    • Character Set
    • Tokens in Python
    • Python Execution Mode
    • Variable And Identifiers
    • Data Types in Python
    • Operators And Expressions
    • Constants in Python
    • Assignment Statement
    • Input / Output in Python
    • Simple Python Scripts
    • Namespace in Python
    • 📝 Assignments
  • Operators in Python
    • Arithmetic Operators
    • Assignment Operators
    • Shorthand Assignment Operators
    • Relational Operators
    • Logical Operators
    • Bitwise Operators
    • Special Operators
    • 📝 Assignments
  • Input Output in Python
    • Accept Input
    • Output Formatting
    • 📝 Assignments
  • Conditional Statement
    • Decision Making
    • if Statement
    • IF-ELSE STATEMENT
    • IF-ELSE LADDER
    • NESTED IF-ELSE
    • Short Hand IF-ELSE
    • 📝 Assignments
  • Loops
    • Introduction to Loops
    • While Loop
    • Nested While Loop
    • 📝 While Loop Assignments
    • For Loop
    • For Loop Examples
    • Nested For Loop
    • Nested For Loop Examples
    • Infinite While Loops
    • Infinite For Loops
    • Break, Continue and Else in Loops
    • Difference Between For and While Loop
    • For Each Loop
    • 📝 For Each Assignments
    • 📝 All Loops Assignments
  • List
    • List in Python
    • Access List Elements
    • List Functions
    • Iterate (Loop) List
    • List Comprehension
    • 📝 Assignments
  • Tuple
    • Tuple in Python
    • Access Tuple Elements
    • Tuple Functions
    • Iterate (Loop) Tuple
    • Unpack Tuple
    • Tuple Comprehension
    • 📝 Assignments
  • Set
    • Set in Python
    • Access Set Elements
    • Set Methods
    • Iterate (Loop) Set
    • Pack/Unpack Set
    • Set Comprehension
    • 📝 Assignments
  • Dictionary
    • Dictionary
    • Access Dictionary Items
    • Dictionary Methods
    • Iterate (Loop) Dictionary
    • Formatting Dictionaries
    • Nested Dictionaries
    • Dictionary Comprehension
    • 📝 Assignments
  • Diff List Tuple Set Dictionary
    • List vs Tuple
    • List vs Set
    • List vs Dictionary
    • Tuple vs Set
    • Tuple vs Dictionary
    • Dictionary vs Set
    • 📝 Assignments
  • Exception
    • Error vs Exception
    • Types of Exception
    • Exception Handling
    • User Defined Exception
    • Logging Exception
    • 📝 Assignments
  • Functions
    • Introduction to Functions
    • Modular Programming
    • Types of Functions
    • Inbuilt Functions
    • Need For User-Defined Function
    • Elements of User Defined Function
    • Function Arguments
    • Nesting of Functions
    • Recursion
    • Global Local and Non Local
    • Python Lambda Functions
    • 📝 Assignments
  • Python Module
    • Introduction to Module
    • Inbuilt Modules in Python
    • User Defined Module
    • 📝 Assignments
  • File Handling
    • Introduction to Files
    • Create File
    • Read Files
    • Write to File
    • Rename File
    • Copy File
    • Move File
    • List Files in Directory
    • Binary Files
    • Zipping and Unzipping Files
    • 📝 Assignments
  • Strings
    • Basics of Strings
    • String Special Operators
    • String Formatting Operators
    • String Methods
    • 📝 Assignments
  • Regular Expressions
  • Python OOPS
    • Basics of Object Oriented
    • What are Classes and Objects?
    • Creating Class and Object
    • OOP vs Procedural Programming
    • Difference Between Classes and Objects
    • Constructors
    • Destructor
    • Built Class Methods and Attributes
    • Class and Instance Variables
    • Inheritance in Python
    • Single Inheritance
    • Multiple Inheritance
    • Multilevel Inheritance
    • Hierarchical Inheritance
    • Hybrid Inheritance
    • Abstraction
    • Method Overriding
    • Abstract Method
    • Interfaces in Python
    • Abstract Class vs Interface
    • Public, Private and Protected
    • Overloading vs Overriding
    • Inheritance vs Composition
    • Encapsulation
    • Polymorphism
    • Inner Classes
    • 📝 Assignments
  • Advanced Python
    • Iterator in Python
    • Generator in Python
    • Decorator in Python
    • Type Hints in Python
    • Async/Await Programming
    • Dataclasses in Python
    • Context Managers in Python
    • Property Decorator in Python
    • __slots__ in Python
    • Enums in Python
    • Walrus Operator in Python
    • Match-Case in Python
    • 📝 Assignments
  • Python Standard Library
    • Collections Module
    • Itertools Module
    • Functools Module
    • Datetime Module
    • JSON Module
    • OS Module
    • Sys Module
    • Random Module
    • Math Module
    • 📝 Assignments
  • Python Testing
    • Unit Testing in Python
    • Pytest Framework
    • Mocking in Python
    • 📝 Assignments
  • Python Best Practices
    • PEP 8 Style Guide
    • Docstrings in Python
    • Logging in Python
    • Code Optimization Tips
    • Debugging Techniques
    • 📝 Assignments
  • MySQL Database in Python
    • Introduction to MySQL with Python
    • DBMS vs File System
    • Connecting to MySQL Database
    • Create Database in MySQL
    • Create Table in MySQL
    • Insert Data in MySQL
    • Insert Multiple Rows
    • Select Data from MySQL
    • WHERE Clause in MySQL
    • Update Data in MySQL
    • Delete Data from MySQL
    • Parameterized Queries
    • Transaction Management
    • Error Handling
    • Connection Pooling
    • MySQL Drivers Guide
    • Joins in MySQL
    • Aggregation Functions
    • Backup MySQL Database
    • Best Practices
    • 📝 Assignments
  • MySQL Database Operations
    • SELECT Statement
    • MySQL Operators
    • DDL Statements
    • DML Statements
    • Subqueries
    • JOIN Operations
    • Aggregation
    • Case Study
    • 📝 Assignments
  • Graphics in Python
  • Threads in Python
    • Introduction to Threads
    • Process vs Threads
    • Concurrent Programming & GIL
    • Uses of Threads
    • Creating Threads
    • Single Tasking
    • Multi Tasking
    • Thread Synchronization
    • 📝 Assignments
  • Interview Questions & Answers
  • Python Case Studies
  • Multiple Choice Questions
  • 📝 Practice Problems
Get in Touch
  • tech2dsm@gmail.com

© Sankalan Data Tech

Python Language Interactive Tutorial

Python: Match-Case

Python Match-Case - Complete Guide

Write cleaner conditional logic with pattern matching.

Created by Sankalan Data Tech Team Verified
Data Engineers, Analysts, Scientists & Trainers
Created by experienced Python developers, data engineers, and data scientists to make programming easy through practical examples, real-world experience, and clear explanations.
On this page:
  • What is Match-Case?
  • Why Use Match-Case?
  • Basic Syntax
  • Pattern Types
  • Using Guards
  • Complex Patterns
  • Real-World Example
  • Best Practices
  • Try It Yourself
  • Quick Quiz
  • Frequently Asked Questions
Share this tutorial:
Twitter LinkedIn Facebook WhatsApp Reddit Telegram Email Copy Link
What You'll Learn Here
  • What is match-case — Python's powerful pattern matching feature
  • Why use it — cleaner than if-elif-else chains
  • Basic syntax — how to write match-case statements
  • Pattern types — literals, variables, sequences, and more
  • Guards — adding extra conditions
  • Real-world examples — practical use cases

What is Match-Case?

The match-case statement is a powerful feature introduced in Python 3.10. It's like a switch statement you might know from other languages, but much more powerful. It lets you match a value against multiple patterns and execute code based on which pattern matches.

Think of match-case like a sorting machine. You put something in, and it checks: "Is it an apple? Then put it in the fruit bin. Is it a carrot? Then put it in the vegetable bin." Each case is a different bin for a different kind of item.

Match-case is much more powerful than a simple switch statement because it can match not just values but also patterns in data structures.

💡 Key concept: Match-case lets you compare a value against multiple patterns and run the code for the first matching pattern.

Why Use Match-Case?

1

The Benefits of Match-Case

Match-case makes your code cleaner and more readable compared to long if-elif chains.

# Why Use Match-Case?

print("=" * 50)
print("WHY USE MATCH-CASE?")
print("=" * 50)

# ============================================================
# WITHOUT MATCH-CASE — Long If-Elif Chain
# ============================================================

print("\n1. WITHOUT MATCH-CASE")

def get_status_color_old(status):
    if status == "success":
        return "green"
    elif status == "error":
        return "red"
    elif status == "warning":
        return "yellow"
    elif status == "info":
        return "blue"
    elif status == "pending":
        return "orange"
    else:
        return "gray"

print(f"   get_status_color_old('success'): {get_status_color_old('success')}")
print(f"   get_status_color_old('error'): {get_status_color_old('error')}")

# Problems:
# 1. Lots of repetition
# 2. Easy to miss a case
# 3. Harder to read with many cases


# ============================================================
# WITH MATCH-CASE — Clean and Readable
# ============================================================

print("\n2. WITH MATCH-CASE")

def get_status_color_new(status):
    match status:
        case "success":
            return "green"
        case "error":
            return "red"
        case "warning":
            return "yellow"
        case "info":
            return "blue"
        case "pending":
            return "orange"
        case _:
            return "gray"

print(f"   get_status_color_new('success'): {get_status_color_new('success')}")
print(f"   get_status_color_new('error'): {get_status_color_new('error')}")

print("   Benefits:")
print("   - No repetition of 'elif'")
print("   - Easier to read")
print("   - Cases are clearly separated")
print("   - _ is the default (like else)")

# ============================================================
# COMPARISON
# ============================================================

print("\n" + "-" * 30)
print("COMPARISON")
print("-" * 30)
print("""
- Cleaner than if-elif chains
- More readable
- Supports pattern matching (not just values)
- Can match on structure (tuples, lists, dicts)
- Default case with _
- Easier to maintain
""")

Benefits of match-case:

  • Cleaner code — no repetitive elifs
  • More readable — cases are clearly separated
  • Pattern matching — match on structure, not just values
  • Default case — use _ for catch-all
  • Easier to maintain — adding new cases is simple

Quick Check: What does the _ case do in match-case? (Answer: It acts as the default case, like else)

Basic Syntax

2

How to Write Match-Case

The syntax is simple: match value: followed by case pattern: blocks.

# Basic Match-Case Syntax

print("=" * 50)
print("BASIC MATCH-CASE SYNTAX")
print("=" * 50)

# ============================================================
# SIMPLE VALUE MATCHING
# ============================================================

print("\n1. SIMPLE VALUE MATCHING")

def get_day_name(day_number):
    match day_number:
        case 1:
            return "Monday"
        case 2:
            return "Tuesday"
        case 3:
            return "Wednesday"
        case 4:
            return "Thursday"
        case 5:
            return "Friday"
        case 6:
            return "Saturday"
        case 7:
            return "Sunday"
        case _:
            return "Invalid day"

print(f"   Day 1: {get_day_name(1)}")
print(f"   Day 5: {get_day_name(5)}")
print(f"   Day 10: {get_day_name(10)}")


# ============================================================
# MATCHING STRINGS
# ============================================================

print("\n2. MATCHING STRINGS")

def handle_command(command):
    match command.lower():
        case "start":
            return "Starting the system..."
        case "stop":
            return "Stopping the system..."
        case "restart":
            return "Restarting the system..."
        case "status":
            return "System is running"
        case "help":
            return "Available commands: start, stop, restart, status, help"
        case _:
            return f"Unknown command: {command}"

print(f"   start: {handle_command('start')}")
print(f"   help: {handle_command('help')}")
print(f"   unknown: {handle_command('unknown')}")


# ============================================================
# MATCHING MULTIPLE VALUES
# ============================================================

print("\n3. MATCHING MULTIPLE VALUES")

def get_response_code_type(code):
    match code:
        case 200 | 201 | 202:
            return "Success"
        case 301 | 302 | 307:
            return "Redirect"
        case 400 | 401 | 403 | 404:
            return "Client Error"
        case 500 | 501 | 502 | 503:
            return "Server Error"
        case _:
            return "Unknown"

print(f"   200: {get_response_code_type(200)}")
print(f"   404: {get_response_code_type(404)}")
print(f"   500: {get_response_code_type(500)}")
print(f"   999: {get_response_code_type(999)}")


# ============================================================
# PATTERN VARIABLES
# ============================================================

print("\n4. PATTERN VARIABLES")

def describe_point(point):
    match point:
        case (0, 0):
            return "Origin"
        case (0, y):
            return f"On Y-axis at y={y}"
        case (x, 0):
            return f"On X-axis at x={x}"
        case (x, y):
            return f"Point at ({x}, {y})"
        case _:
            return "Invalid point"

print(f"   (0, 0): {describe_point((0, 0))}")
print(f"   (0, 5): {describe_point((0, 5))}")
print(f"   (3, 0): {describe_point((3, 0))}")
print(f"   (2, 3): {describe_point((2, 3))}")


# ============================================================
# RULES TO REMEMBER
# ============================================================

print("\n" + "-" * 30)
print("RULES FOR MATCH-CASE")
print("-" * 30)
print("""
- match value: starts the matching
- case pattern: defines a pattern to match
- _ is the default (catch-all) case
- Patterns are checked in order
- First matching pattern is executed
- Use | for OR patterns (case 1 | 2:)
- Variables in patterns capture values
""")

Basic syntax key points:

  • match value: — starts the matching
  • case pattern: — defines a pattern to match
  • _ — default/catch-all case
  • | — OR pattern (case 1 | 2:)
  • Variables — capture values from patterns

Quick Check: How do you match multiple values in one case? (Answer: Use | like case 1 | 2 | 3:)

Pattern Types

3

Different Kinds of Patterns

Match-case supports many different pattern types. Let's look at the most common ones.

# Pattern Types in Match-Case

print("=" * 50)
print("PATTERN TYPES")
print("=" * 50)

# ============================================================
# 1. LITERAL PATTERNS
# ============================================================

print("\n1. LITERAL PATTERNS")

def check_value(value):
    match value:
        case 0:
            return "Zero"
        case 1:
            return "One"
        case True:
            return "True"
        case False:
            return "False"
        case "hello":
            return "Hello string"
        case None:
            return "None value"
        case _:
            return "Something else"

print(f"   0: {check_value(0)}")
print(f"   1: {check_value(1)}")
print(f"   True: {check_value(True)}")
print(f"   'hello': {check_value('hello')}")
print(f"   None: {check_value(None)}")


# ============================================================
# 2. VARIABLE PATTERNS (captures value)
# ============================================================

print("\n2. VARIABLE PATTERNS")

def describe_number(num):
    match num:
        case 0:
            return "Zero"
        case n if n < 0:
            return f"Negative number: {n}"
        case n if n > 0:
            return f"Positive number: {n}"

print(f"   5: {describe_number(5)}")
print(f"   -3: {describe_number(-3)}")
print(f"   0: {describe_number(0)}")


# ============================================================
# 3. SEQUENCE PATTERNS (lists, tuples)
# ============================================================

print("\n3. SEQUENCE PATTERNS")

def process_list(items):
    match items:
        case []:
            return "Empty list"
        case [x]:
            return f"Single item: {x}"
        case [x, y]:
            return f"Two items: {x} and {y}"
        case [x, y, *rest]:
            return f"First: {x}, Second: {y}, Rest: {rest}"
        case _:
            return "Something else"

print(f"   []: {process_list([])}")
print(f"   [5]: {process_list([5])}")
print(f"   [1, 2]: {process_list([1, 2])}")
print(f"   [1, 2, 3, 4]: {process_list([1, 2, 3, 4])}")


# ============================================================
# 4. MAPPING PATTERNS (dictionaries)
# ============================================================

print("\n4. MAPPING PATTERNS")

def process_user(user):
    match user:
        case {"name": name, "age": age}:
            return f"User {name} is {age} years old"
        case {"name": name}:
            return f"User {name} (age unknown)"
        case {"age": age}:
            return f"Age: {age} (name unknown)"
        case _:
            return "Invalid user data"

print(f"   {{'name': 'Alice', 'age': 30}}: {process_user({'name': 'Alice', 'age': 30})}")
print(f"   {{'name': 'Bob'}}: {process_user({'name': 'Bob'})}")
print(f"   {{'age': 25}}: {process_user({'age': 25})}")


# ============================================================
# 5. CLASS PATTERNS
# ============================================================

print("\n5. CLASS PATTERNS")

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

def process_point(p):
    match p:
        case Point(x=0, y=0):
            return "Origin"
        case Point(x=0, y=y):
            return f"On Y-axis at y={y}"
        case Point(x=x, y=0):
            return f"On X-axis at x={x}"
        case Point(x=x, y=y):
            return f"Point ({x}, {y})"
        case _:
            return "Not a point"

p1 = Point(0, 0)
p2 = Point(0, 5)
p3 = Point(3, 4)

print(f"   (0, 0): {process_point(p1)}")
print(f"   (0, 5): {process_point(p2)}")
print(f"   (3, 4): {process_point(p3)}")


# ============================================================
# 6. OR PATTERNS
# ============================================================

print("\n6. OR PATTERNS")

def classify_number(n):
    match n:
        case 0 | 1 | 2:
            return "Small (0-2)"
        case 3 | 4 | 5:
            return "Medium (3-5)"
        case 6 | 7 | 8 | 9:
            return "Large (6-9)"
        case _:
            return "Other"

print(f"   1: {classify_number(1)}")
print(f"   4: {classify_number(4)}")
print(f"   8: {classify_number(8)}")
print(f"   10: {classify_number(10)}")


# ============================================================
# 7. WILDCARD PATTERN
# ============================================================

print("\n7. WILDCARD PATTERN (_)")

def wildcard_demo(value):
    match value:
        case 1:
            return "One"
        case 2:
            return "Two"
        case _:
            return "Something else (wildcard)"

print(f"   1: {wildcard_demo(1)}")
print(f"   2: {wildcard_demo(2)}")
print(f"   3: {wildcard_demo(3)}")

Pattern types key points:

  • Literal — exact values like 1, "hello", True
  • Variable — captures the value (e.g., case n:)
  • Sequence — lists and tuples with *rest
  • Mapping — dictionaries with keys
  • Class — matches class instances
  • OR — multiple patterns with |
  • Wildcard — _ for anything

Quick Check: What pattern type would you use to match a dictionary with specific keys? (Answer: Mapping pattern like case {"name": name}:)

Using Guards

4

Adding Extra Conditions

Guards let you add extra conditions to your patterns using if after the pattern.

# Guards in Match-Case

print("=" * 50)
print("GUARDS IN MATCH-CASE")
print("=" * 50)

# ============================================================
# BASIC GUARDS
# ============================================================

print("\n1. BASIC GUARDS")

def categorize_age(age):
    match age:
        case a if a < 0:
            return "Invalid age"
        case a if a < 13:
            return "Child"
        case a if a < 18:
            return "Teenager"
        case a if a < 65:
            return "Adult"
        case a if a < 120:
            return "Senior"
        case _:
            return "Invalid age"

print(f"   -5: {categorize_age(-5)}")
print(f"   10: {categorize_age(10)}")
print(f"   16: {categorize_age(16)}")
print(f"   30: {categorize_age(30)}")
print(f"   70: {categorize_age(70)}")
print(f"   150: {categorize_age(150)}")


# ============================================================
# GUARDS WITH PATTERN VARIABLES
# ============================================================

print("\n2. GUARDS WITH PATTERN VARIABLES")

def evaluate_point(point):
    match point:
        case (x, y) if x == y:
            return f"On diagonal: ({x}, {y})"
        case (x, y) if x > y:
            return f"Above diagonal: ({x}, {y})"
        case (x, y) if x < y:
            return f"Below diagonal: ({x}, {y})"
        case _:
            return "Not a point"

print(f"   (3, 3): {evaluate_point((3, 3))}")
print(f"   (5, 2): {evaluate_point((5, 2))}")
print(f"   (2, 5): {evaluate_point((2, 5))}")


# ============================================================
# GUARDS WITH DICTIONARY PATTERNS
# ============================================================

print("\n3. GUARDS WITH DICTIONARY PATTERNS")

def validate_user(user):
    match user:
        case {"name": name, "age": age} if age >= 18:
            return f"Adult user: {name} ({age})"
        case {"name": name, "age": age} if age < 18:
            return f"Minor user: {name} ({age})"
        case {"name": name}:
            return f"User: {name} (age unknown)"
        case _:
            return "Invalid user data"

print(f"   {{'name': 'Alice', 'age': 25}}: {validate_user({'name': 'Alice', 'age': 25})}")
print(f"   {{'name': 'Bob', 'age': 15}}: {validate_user({'name': 'Bob', 'age': 15})}")
print(f"   {{'name': 'Charlie'}}: {validate_user({'name': 'Charlie'})}")


# ============================================================
# GUARDS WITH SEQUENCE PATTERNS
# ============================================================

print("\n4. GUARDS WITH SEQUENCE PATTERNS")

def analyze_numbers(numbers):
    match numbers:
        case [x, y, z] if x + y == z:
            return f"{x} + {y} = {z}"
        case [x, y, z] if x * y == z:
            return f"{x} * {y} = {z}"
        case [x, y, z] if x == y == z:
            return f"All equal: {x}"
        case [x, y, z]:
            return f"Numbers: {x}, {y}, {z}"
        case _:
            return "Invalid sequence"

print(f"   [2, 3, 5]: {analyze_numbers([2, 3, 5])}")
print(f"   [2, 3, 6]: {analyze_numbers([2, 3, 6])}")
print(f"   [3, 3, 3]: {analyze_numbers([3, 3, 3])}")
print(f"   [1, 4, 7]: {analyze_numbers([1, 4, 7])}")


# ============================================================
# MULTIPLE GUARDS
# ============================================================

print("\n5. MULTIPLE GUARDS")

def classify_triangle(sides):
    match sides:
        case [a, b, c] if a <= 0 or b <= 0 or c <= 0:
            return "Invalid triangle (negative sides)"
        case [a, b, c] if a + b <= c or a + c <= b or b + c <= a:
            return "Not a triangle"
        case [a, b, c] if a == b == c:
            return "Equilateral triangle"
        case [a, b, c] if a == b or b == c or a == c:
            return "Isosceles triangle"
        case [a, b, c]:
            return "Scalene triangle"

print(f"   [3, 4, 5]: {classify_triangle([3, 4, 5])}")
print(f"   [3, 3, 3]: {classify_triangle([3, 3, 3])}")
print(f"   [3, 3, 5]: {classify_triangle([3, 3, 5])}")
print(f"   [1, 1, 3]: {classify_triangle([1, 1, 3])}")

Guards key points:

  • if condition — adds extra conditions to patterns
  • Pattern variables — can be used in guards
  • Complex logic — guards can use any boolean expression
  • Order matters — more specific guards should come first
  • Readability — guards keep conditions with their patterns

Quick Check: What is a guard in match-case? (Answer: An if condition after a pattern that adds an extra condition)

Complex Patterns

5

Advanced Pattern Matching

Match-case can handle complex nested patterns, making it very powerful for parsing data.

# Complex Patterns in Match-Case

print("=" * 50)
print("COMPLEX PATTERNS")
print("=" * 50)

# ============================================================
# NESTED PATTERNS
# ============================================================

print("\n1. NESTED PATTERNS")

def process_data(data):
    match data:
        case {"user": {"name": name, "age": age}, "status": status}:
            return f"User {name} ({age}) has status: {status}"
        case {"user": {"name": name}, "status": status}:
            return f"User {name} (age unknown) has status: {status}"
        case {"status": status}:
            return f"Unknown user with status: {status}"
        case _:
            return "Invalid data format"

data1 = {"user": {"name": "Alice", "age": 30}, "status": "active"}
data2 = {"user": {"name": "Bob"}, "status": "inactive"}
data3 = {"status": "pending"}

print(f"   Data1: {process_data(data1)}")
print(f"   Data2: {process_data(data2)}")
print(f"   Data3: {process_data(data3)}")


# ============================================================
# LIST WITH PATTERNS
# ============================================================

print("\n2. LIST WITH PATTERNS")

def parse_expression(expr):
    match expr:
        case ["add", a, b]:
            return f"{a} + {b} = {a + b}"
        case ["sub", a, b]:
            return f"{a} - {b} = {a - b}"
        case ["mul", a, b]:
            return f"{a} * {b} = {a * b}"
        case ["div", a, 0]:
            return "Division by zero error"
        case ["div", a, b]:
            return f"{a} / {b} = {a / b}"
        case ["pow", a, b]:
            return f"{a} ^ {b} = {a ** b}"
        case _:
            return "Unknown operation"

print(f"   ['add', 5, 3]: {parse_expression(['add', 5, 3])}")
print(f"   ['mul', 4, 2]: {parse_expression(['mul', 4, 2])}")
print(f"   ['div', 10, 0]: {parse_expression(['div', 10, 0])}")
print(f"   ['pow', 2, 3]: {parse_expression(['pow', 2, 3])}")


# ============================================================
# MIXED PATTERNS
# ============================================================

print("\n3. MIXED PATTERNS")

def analyze_value(value):
    match value:
        case int() as n if n > 0:
            return f"Positive integer: {n}"
        case int() as n if n < 0:
            return f"Negative integer: {n}"
        case float() as f:
            return f"Float: {f:.2f}"
        case str() as s:
            return f"String: {s}"
        case list() as lst:
            return f"List with {len(lst)} items"
        case dict() as d:
            return f"Dictionary with {len(d)} keys"
        case _:
            return "Unknown type"

print(f"   42: {analyze_value(42)}")
print(f"   -5: {analyze_value(-5)}")
print(f"   3.14: {analyze_value(3.14)}")
print(f"   'hello': {analyze_value('hello')}")
print(f"   [1, 2, 3]: {analyze_value([1, 2, 3])}")
print(f"   {{'a': 1}}: {analyze_value({'a': 1})}")


# ============================================================
# AS PATTERNS (binding)
# ============================================================

print("\n4. AS PATTERNS (binding)")

def process_with_as(value):
    match value:
        case [1, 2, 3] as whole:
            return f"Matched [1, 2, 3] as {whole}"
        case {"name": name, "age": age} as person:
            return f"Person: {person}"
        case _:
            return "No match"

print(f"   [1, 2, 3]: {process_with_as([1, 2, 3])}")
print(f"   {{'name': 'Alice', 'age': 30}}: {process_with_as({'name': 'Alice', 'age': 30})}")


# ============================================================
# NESTED MAPPINGS WITH VARIABLES
# ============================================================

print("\n5. NESTED MAPPINGS WITH VARIABLES")

def process_config(config):
    match config:
        case {"database": {"host": host, "port": port}}:
            return f"Database: {host}:{port}"
        case {"database": {"host": host}}:
            return f"Database: {host} (default port)"
        case {"cache": {"type": "redis", "host": host}}:
            return f"Redis cache at {host}"
        case {"cache": {"type": "memcached"}}:
            return "Memcached cache"
        case _:
            return "Unknown config"

config1 = {"database": {"host": "localhost", "port": 5432}}
config2 = {"database": {"host": "server.com"}}
config3 = {"cache": {"type": "redis", "host": "cache.local"}}
config4 = {"cache": {"type": "memcached"}}

print(f"   Config1: {process_config(config1)}")
print(f"   Config2: {process_config(config2)}")
print(f"   Config3: {process_config(config3)}")
print(f"   Config4: {process_config(config4)}")

Complex patterns key points:

  • Nested patterns — match nested structures
  • Type checking — use int() as n to match types
  • as pattern — bind the whole matched object
  • Mixed patterns — combine different pattern types
  • Flexible — handle complex data structures

Quick Check: What does as do in a pattern? (Answer: It binds the matched object to a variable)

Real-World Example

6

Building a JSON Parser

# Real-World Example: JSON Data Processor

import json
from datetime import datetime

print("=" * 60)
print("JSON DATA PROCESSOR")
print("=" * 60)

# ============================================================
# SAMPLE DATA
# ============================================================

sample_data = {
    "type": "user_event",
    "user": {
        "id": 123,
        "name": "Alice Johnson",
        "email": "alice@example.com",
        "age": 30,
        "preferences": {
            "theme": "dark",
            "notifications": True
        }
    },
    "event": {
        "type": "login",
        "timestamp": "2024-01-15T10:30:00",
        "details": {
            "ip": "192.168.1.1",
            "device": "Chrome"
        }
    },
    "metadata": {
        "source": "web",
        "version": "2.0"
    }
}

print("\n1. PROCESSING USER DATA")

def process_user_data(data):
    match data:
        case {
            "user": {
                "name": name,
                "email": email,
                "age": age,
                "preferences": {"theme": theme, "notifications": notifications}
            },
            "metadata": {"source": source}
        } if age >= 18:
            return {
                "name": name,
                "email": email,
                "age": age,
                "theme": theme,
                "notifications": notifications,
                "source": source,
                "status": "valid_adult"
            }
        case {
            "user": {
                "name": name,
                "email": email,
                "age": age,
                "preferences": preferences
            }
        } if age < 18:
            return {
                "name": name,
                "email": email,
                "age": age,
                "status": "minor",
                "message": "Parental consent required"
            }
        case {"user": user_data}:
            return {"status": "user_found", "data": user_data}
        case _:
            return {"status": "invalid", "message": "Invalid user data"}

result = process_user_data(sample_data)
print("   Processed user data:")
for key, value in result.items():
    print(f"      {key}: {value}")

print("\n2. PROCESSING EVENTS")

def process_event(data):
    match data:
        case {
            "event": {
                "type": "login",
                "timestamp": timestamp,
                "details": {"ip": ip, "device": device}
            }
        }:
            return {
                "event_type": "login",
                "timestamp": timestamp,
                "ip": ip,
                "device": device,
                "status": "login_event"
            }
        case {
            "event": {
                "type": "purchase",
                "details": {"item": item, "price": price}
            }
        }:
            return {
                "event_type": "purchase",
                "item": item,
                "price": price,
                "status": "purchase_event"
            }
        case {"event": event_data}:
            return {"event_type": "unknown", "data": event_data}
        case _:
            return {"status": "invalid", "message": "No event data"}

event_result = process_event(sample_data)
print("   Processed event:")
for key, value in event_result.items():
    print(f"      {key}: {value}")

print("\n3. PROCESSING RESPONSES")

def process_response(response):
    match response:
        case {"status": "success", "data": data}:
            return {"status": "OK", "data": data}
        case {"status": "error", "code": 400, "message": msg}:
            return {"status": "ERROR", "code": 400, "message": f"Bad Request: {msg}"}
        case {"status": "error", "code": 404}:
            return {"status": "ERROR", "code": 404, "message": "Not Found"}
        case {"status": "error", "code": code}:
            return {"status": "ERROR", "code": code, "message": "Server Error"}
        case _:
            return {"status": "UNKNOWN", "message": "Invalid response"}

responses = [
    {"status": "success", "data": {"id": 1, "name": "Item"}},
    {"status": "error", "code": 400, "message": "Invalid input"},
    {"status": "error", "code": 404},
    {"status": "error", "code": 500}
]

print("   Processing responses:")
for i, resp in enumerate(responses, 1):
    result = process_response(resp)
    print(f"      Response {i}: {result}")

print("\n" + "=" * 60)
print("KEY TAKEAWAYS:")
print("=" * 60)
print("""
- Match-case is perfect for processing JSON data
- Handles nested structures easily
- Guards add validation conditions
- Clean and readable code
- Less error-prone than manual checks
""")

Real-world example key points:

  • User data — match nested user profiles
  • Events — handle different event types
  • Responses — process API responses
  • Validation — guards check conditions
  • Clean code — no complex if-else chains

Quick Check: What's a good use case for match-case? (Answer: Processing JSON data, API responses, and complex data structures)

Best Practices

7

Using Match-Case Effectively

# Best Practices for Match-Case

print("=" * 60)
print("BEST PRACTICES FOR MATCH-CASE")
print("=" * 60)

# ============================================================
# 1. USE FOR EXHAUSTIVE PATTERN MATCHING
# ============================================================

print("\n1. USE FOR EXHAUSTIVE PATTERN MATCHING")

# Good - covers all cases
def process_result(result):
    match result:
        case {"status": "ok", "data": data}:
            return data
        case {"status": "error", "message": msg}:
            return f"Error: {msg}"
        case _:
            return "Unknown result"

# Bad - missing case (no default)
# def process_result_bad(result):
#     match result:
#         case {"status": "ok", "data": data}:
#             return data

print("   Always include a default case")


# ============================================================
# 2. ORDER PATTERNS FROM SPECIFIC TO GENERAL
# ============================================================

print("\n2. ORDER PATTERNS FROM SPECIFIC TO GENERAL")

def process_value(value):
    match value:
        case 0:  # Most specific first
            return "Zero"
        case int():  # More general
            return f"Integer: {value}"
        case str():  # Even more general
            return f"String: {value}"
        case _:  # Most general last
            return "Unknown"

print("   Specific patterns first, general last")


# ============================================================
# 3. USE GUARDS FOR EXTRA CONDITIONS
# ============================================================

print("\n3. USE GUARDS FOR EXTRA CONDITIONS")

def classify_number(n):
    match n:
        case x if x == 0:
            return "Zero"
        case x if x > 0:
            return "Positive"
        case x if x < 0:
            return "Negative"

print("   Guards add extra conditions to patterns")


# ============================================================
# 4. KEEP IT READABLE
# ============================================================

print("\n4. KEEP IT READABLE")

# Good - clear and readable
def process_user_data_good(data):
    match data:
        case {"name": name, "age": age}:
            return f"{name} is {age} years old"
        case {"name": name}:
            return f"{name} (age unknown)"
        case _:
            return "Invalid user"

# Bad - too complex and hard to read
# def process_user_data_bad(data):
#     match data:
#         case {"name": name, "age": age, "city": city, "country": country, "phone": phone} if age > 18 and city == "NYC":
#             return f"{name} ({phone})"
#         case _:
#             return "Invalid"

print("   Keep patterns simple and readable")


# ============================================================
# 5. USE TYPE CHECKING PATTERNS
# ============================================================

print("\n5. USE TYPE CHECKING PATTERNS")

def process_value_type(value):
    match value:
        case int() as n:
            return f"Integer: {n}"
        case float() as f:
            return f"Float: {f}"
        case str() as s:
            return f"String: {s}"
        case list() as l:
            return f"List: {l}"
        case _:
            return "Unknown type"

print("   Type checking patterns make code cleaner")


# ============================================================
# 6. SUMMARY
# ============================================================

print("\n" + "=" * 60)
print("BEST PRACTICES SUMMARY")
print("=" * 60)
print("""
- Always include a default case
- Order from specific to general
- Use guards for extra conditions
- Keep patterns readable
- Use type checking patterns
- Don't overcomplicate
- Match-case is for readability
""")

Best practices summary:

  • Always include default — use _ case
  • Order matters — specific patterns first
  • Use guards — for extra conditions
  • Keep it readable — don't overcomplicate
  • Type checking — use int() as n patterns
  • Don't overuse — match-case for readability

Quick Check: Should you always include a default case? (Answer: Yes, use _ to handle unexpected values)

Try It Yourself

Experiment with match-case in the editor below.

Loading Pyodide... 0%
Python Code Editor
==================================================
MATCH-CASE - PRACTICE
==================================================

1. BASIC MATCH-CASE
'active': User is active
'unknown': Unknown status

2. MATCHING NUMBERS WITH GUARDS
95: A
75: C
50: F
-5: Invalid score

3. MATCHING TUPLES
(0, 0): Origin
(0, 5): Y-axis at 5
(3, 0): X-axis at 3
(2, 3): Point (2, 3)

4. MATCHING DICTIONARIES
{'name': 'Alice', 'age': 30}: Alice is 30 years old
{'name': 'Bob'}: Bob (age unknown)
{}: Invalid person data
🏆

You've Got It!

You now understand match-case in Python. You know how to use pattern matching, guards, and complex patterns for cleaner code.

Quick Quiz

Test what you've learned:

1. What version of Python introduced match-case?
2. What does the _ case do in match-case?
3. What is a guard in match-case?
4. How do you match multiple values in one case?
5. When should you use match-case?

Frequently Asked Questions

What is match-case in Python? ▼

Match-case is a pattern matching feature introduced in Python 3.10. It allows you to match a value against multiple patterns and execute code based on the first matching pattern. It's more powerful than a traditional switch statement.

How is match-case different from if-elif? ▼

Match-case is cleaner and more readable for multiple conditions. It also supports pattern matching on structures (like tuples, lists, dictionaries), not just values. It's especially useful for complex data structures.

Can I use match-case in older Python versions? ▼

No, match-case requires Python 3.10 or later. If you're using an older version, you'll get a syntax error. Consider upgrading or using if-elif chains for older versions.

What patterns can I match? ▼

You can match many things: literals (numbers, strings), variables, sequences (lists, tuples), mappings (dictionaries), class instances, and more. You can also use guards (if conditions) and OR patterns (|).

Is match-case faster than if-elif? ▼

In most cases, the performance difference is negligible. Match-case is primarily for readability and expressiveness, not speed. Use it where it makes your code cleaner and more maintainable.

Can I use match-case with walrus operator? ▼

Yes, you can combine match-case with the walrus operator in guards. For example: case x if (y := x + 1) > 5: This can make your patterns even more powerful.

Where to Go From Here

Now that you understand match-case in Python, check out these related topics:

Walrus Operator

Learn about the walrus operator (:=) for assignment expressions.

Learn More →

Decorators

Learn about decorators — another advanced Python feature.

Learn More →

Type Hints

Learn about type hints and how they work with pattern matching.

Learn More →
Interview Resources
  • Python Syntax & Variables Interview Questions
  • Top SQL Interview Questions & Answers
  • SQL Joins: Displaying Data from Multiple Tables FAQ
  • Python Lists and Dictionaries Interview Questions
  • Python OOP Interview Questions
  • SQL Set Operators Interview Questions
Previous: Walrus Operator Next: Advanced Assignments →