Pythonic Practices and 'match' Statements
From the csc241\ curriculum
TL;DR
"Pythonic" code means writing clear, efficient, and idiomatic Python that leverages the language's features. The match statement, introduced in Python 3.10, provides a powerful and readable way to handle conditional logic based on structural pattern matching. Mastering these practices helps you write more maintainable and effective Python programs.
1. The Mental Model
Think of "Pythonic" as speaking Python fluently, not just translating from another language. The match statement is like a smart dispatcher that looks at the structure of your data and sends it to the right handler.
2. The Core Material
What is "Pythonic"?

Photo by Prajwal Bajracharya on Pexels
"Pythonic" refers to code that adheres to the common idioms and best practices of the Python community. It's about writing code that is:
- Readable: Easy for others (and your future self) to understand.
- Concise: Achieves its goal with minimal, clear code.
- Efficient: Performs well without unnecessary complexity.
- Idiomatic: Uses Python's built-in features and common patterns effectively.
A great resource for understanding Pythonic principles is PEP 20, "The Zen of Python," which you can access by typing import this in your Python interpreter.
Here are some common Pythonic practices:
- List Comprehensions: For creating lists concisely.
- Unpythonic:
python squares = [] for i in range(5): squares.append(i * i) - Pythonic:
python squares = [i * i for i in range(5)]
- Unpythonic:
enumerate(): For iterating with an index.- Unpythonic:
python my_list = ['a', 'b', 'c'] for i in range(len(my_list)): print(f"{i}: {my_list[i]}") - Pythonic:
python my_list = ['a', 'b', 'c'] for i, item in enumerate(my_list): print(f"{i}: {item}")
- Unpythonic:
withstatements: For proper resource management (files, locks).- Unpythonic:
python f = open("myfile.txt", "r") data = f.read() f.close() # Easy to forget or miss on error - Pythonic:
python with open("myfile.txt", "r") as f: data = f.read() # File is automatically closed here, even if errors occur
- Unpythonic:
The match Statement (Structural Pattern Matching)

Photo by Nataliya Vaitkevich on Pexels
Introduced in Python 3.10, the match statement allows you to compare a value against several possible patterns. It's more powerful than a simple if/elif/else chain, especially when dealing with complex data structures like lists, dictionaries, or objects.
The basic syntax looks like this:
match subject:
case pattern1:
# code to execute if subject matches pattern1
case pattern2:
# code to execute if subject matches pattern2
case _: # The wildcard pattern, like 'else'
# code to execute if no other pattern matches
Here's what match can do:
- Literal Patterns: Match exact values.
python status_code = 200 match status_code: case 200: print("OK") case 404: print("Not Found") case _: print("Unknown status") - Sequence Patterns: Match lists or tuples.
python point = (1, 2) match point: case (0, 0): print("Origin") case (x, 0): print(f"On X-axis at {x}") case (0, y): print(f"On Y-axis at {y}") case (x, y): print(f"At ({x}, {y})")
Notice howxandyact as capture patterns, binding the values from thepointtuple. - Mapping Patterns: Match dictionaries.
python command = {"action": "move", "x": 10, "y": 20} match command: case {"action": "move", "x": x, "y": y}: print(f"Moving to ({x}, {y})") case {"action": "quit"}: print("Exiting") case _: print("Unknown command") -
Class Patterns: Match objects based on their type and attributes.
```python
class Point:
def init(self, x, y):
self.x = x
self.y = yp = Point(5, 10)
match p:
case Point(x=0, y=0):
print("Origin point object")
case Point(x=x, y=y):
print(f"Point object at ({x}, {y})")
* **`if` Guards:** Add extra conditions to a pattern.python
value = 15
match value:
case x if x < 10:
print(f"{x} is less than 10")
case x if x >= 10 and x < 20:
print(f"{x} is between 10 and 19")
case _:
print("Value is 20 or more")
```
Here's a diagram showing the flow of a match statement:
graph TD
A[Start Match] --> B{Evaluate Subject};
B --> C{Does it match Pattern 1?};
C -- Yes --> D[Execute Code for Pattern 1];
C -- No --> E{Does it match Pattern 2?};
E -- Yes --> F[Execute Code for Pattern 2];
E -- No --> G{Does it match Pattern N?};
G -- Yes --> H[Execute Code for Pattern N];
G -- No --> I{Does it match Wildcard (_)?};
I -- Yes --> J[Execute Code for Wildcard];
I -- No --> K[No Match Found (Error or Fallthrough)];
D --> L[End Match];
F --> L;
H --> L;
J --> L;
K --> L;
3. Worked Example
Let's say you're building a simple command processor that receives commands as lists or dictionaries.
def process_command(command):
match command:
case ["move", x, y]:
print(f"Executing move command to ({x}, {y})")
return f"Moved to ({x}, {y})"
case ["draw", shape, color] if shape in ["circle", "square"]:
print(f"Executing draw command: {color} {shape}")
return f"Drew a {color} {shape}"
case {"action": "undo", "steps": n}:
print(f"Undoing {n} steps.")
return f"Undid {n} steps"
case {"action": "quit"}:
print("Quitting application.")
return "Quit"
case _:
print(f"Unknown command: {command}")
return "Error: Unknown command"
# Test cases
print(process_command(["move", 10, 20]))
print(process_command(["draw", "circle", "red"]))
print(process_command(["draw", "triangle", "blue"])) # This won't match the draw pattern due to the guard
print(process_command({"action": "undo", "steps": 5}))
print(process_command({"action": "quit"}))
print(process_command("hello"))
print(process_command(["save"]))
Output:
Executing move command to (10, 20)
Moved to (10, 20)
Executing draw command: red circle
Drew a red circle
Unknown command: ['draw', 'triangle', 'blue']
Error: Unknown command
Undoing 5 steps.
Undid 5 steps
Quitting application.
Quit
Unknown command: hello
Error: Unknown command
Unknown command: ['save']
Error: Unknown command
4. Key Takeaways
- "Pythonic" code is readable, concise, efficient, and uses Python's features idiomatically.
- List comprehensions,
enumerate(), andwithstatements are common Pythonic constructs. - The
matchstatement (Python 3.10+) allows for structural pattern matching, making complex conditional logic cleaner. matchcan handle literals, sequences (lists/tuples), mappings (dictionaries), and class instances.- Capture patterns within
matchcases automatically bind values to variables. ifguards can be added tocasestatements for more specific conditions.- The wildcard
_acts as a catch-all inmatchstatements, similar toelse.
Common Mistakes to Avoid:
- Using match for simple if/elif/else where a direct comparison is clearer.
- Forgetting the wildcard _ case, which can lead to unhandled scenarios.
- Trying to use match on Python versions older than 3.10 (it will raise a SyntaxError).
- Over-complicating patterns; sometimes a series of if statements is still more readable for very simple checks.
5. Now Try It
Write a function called analyze_data_point that takes a single argument, data_point. This data_point can be:
1. A string like "error: File not found"
2. A tuple ("success", value)
3. A dictionary {"type": "event", "id": 123, "details": "User logged in"}
4. Any other type of data.
Use a match statement to:
- If it's a string starting with "error:", print "Error detected: [message]".
- If it's a tuple ("success", value), print "Operation successful with result: [value]".
- If it's a dictionary with "type": "event", print "Event ID [id]: [details]".
- For anything else, print "Unhandled data type: [data_point]".
Success looks like your function correctly identifying and printing the appropriate message for each of the
Frequently asked about Pythonic Practices and 'match' Statements
More from csc241\
Get the full csc241\ curriculum
Clone the complete plan to your dashboard for unlimited AI-generated notes, practice quizzes, and a personalised revision schedule.
Save this course free