Conditionals & Pattern Matching

0/5 in this phase0/54 across the roadmap

📖 Concept

Python's conditional statements are straightforward but have some unique features compared to other languages. Python uses indentation (not braces) to define blocks, and supports structural pattern matching (match-case) since Python 3.10.

Basic if/elif/else: Python uses elif instead of else if. There's no switch statement in Python before 3.10 — use if/elif chains or dictionary dispatch instead.

Ternary (conditional) expression:

result = value_if_true if condition else value_if_false

This is Python's version of condition ? a : b from C/JavaScript.

Structural Pattern Matching (Python 3.10+): The match/case statement is NOT just a switch — it's a powerful pattern matching system that can destructure data, match types, apply guards, and bind variables. It's similar to pattern matching in Rust or Scala.

Key patterns:

  • Literal patterns: match exact values (case 200:)
  • Capture patterns: bind to a variable (case x:)
  • Wildcard pattern: match anything (case _:)
  • Sequence patterns: destructure lists/tuples (case [x, y]:)
  • Mapping patterns: destructure dicts (case {"key": value}:)
  • Class patterns: match object attributes (case Point(x=0, y=y):)
  • OR patterns: match alternatives (case 200 | 201:)
  • Guard clauses: add conditions (case x if x > 0:)

💻 Code Example

codeTap to expand ⛶
1# ============================================================
2# Basic conditionals
3# ============================================================
4age = 25
5
6if age < 13:
7 category = "child"
8elif age < 18:
9 category = "teenager"
10elif age < 65:
11 category = "adult"
12else:
13 category = "senior"
14
15print(f"{age} years old → {category}")
16
17# ============================================================
18# Ternary expression
19# ============================================================
20x = 10
21result = "even" if x % 2 == 0 else "odd"
22print(f"{x} is {result}")
23
24# Nested ternary (avoid in production — hard to read)
25grade = 85
26letter = "A" if grade >= 90 else "B" if grade >= 80 else "C" if grade >= 70 else "F"
27print(f"Grade: {letter}")
28
29# ============================================================
30# Truthiness in conditionals
31# ============================================================
32# Python evaluates these as False:
33# False, None, 0, 0.0, "", [], {}, set(), ()
34
35items = []
36if not items:
37 print("List is empty") # Pythonic way to check emptiness
38
39# BAD: if len(items) == 0:
40# GOOD: if not items:
41
42name = ""
43display = name or "Anonymous" # Short-circuit default
44
45# ============================================================
46# Structural Pattern Matching (Python 3.10+)
47# ============================================================
48
49# Basic value matching
50def http_status(status):
51 match status:
52 case 200:
53 return "OK"
54 case 301:
55 return "Moved Permanently"
56 case 404:
57 return "Not Found"
58 case 500:
59 return "Internal Server Error"
60 case _:
61 return f"Unknown status: {status}"
62
63print(http_status(200)) # "OK"
64print(http_status(418)) # "Unknown status: 418"
65
66# OR patterns
67def classify_status(status):
68 match status:
69 case 200 | 201 | 202:
70 return "Success"
71 case 301 | 302 | 307:
72 return "Redirect"
73 case 400 | 401 | 403 | 404:
74 return "Client Error"
75 case 500 | 502 | 503:
76 return "Server Error"
77 case _:
78 return "Unknown"
79
80# Sequence patterns (destructuring)
81def process_command(command):
82 match command.split():
83 case ["quit"]:
84 return "Quitting..."
85 case ["go", direction]:
86 return f"Going {direction}"
87 case ["get", item, "from", location]:
88 return f"Getting {item} from {location}"
89 case ["drop", *items]:
90 return f"Dropping: {', '.join(items)}"
91 case _:
92 return f"Unknown command: {command}"
93
94print(process_command("go north")) # Going north
95print(process_command("get sword from chest")) # Getting sword from chest
96print(process_command("drop apple banana")) # Dropping: apple, banana
97
98# Mapping patterns (dict destructuring)
99def process_event(event):
100 match event:
101 case {"type": "click", "x": x, "y": y}:
102 return f"Click at ({x}, {y})"
103 case {"type": "keypress", "key": key}:
104 return f"Key pressed: {key}"
105 case {"type": "scroll", "direction": "up" | "down" as dir}:
106 return f"Scrolling {dir}"
107 case _:
108 return "Unknown event"
109
110print(process_event({"type": "click", "x": 100, "y": 200}))
111print(process_event({"type": "keypress", "key": "Enter"}))
112
113# Guard clauses (if conditions on patterns)
114def classify_point(point):
115 match point:
116 case (0, 0):
117 return "Origin"
118 case (x, 0):
119 return f"X-axis at {x}"
120 case (0, y):
121 return f"Y-axis at {y}"
122 case (x, y) if x == y:
123 return f"On diagonal at ({x}, {y})"
124 case (x, y) if x > 0 and y > 0:
125 return f"Quadrant I: ({x}, {y})"
126 case (x, y):
127 return f"Point at ({x}, {y})"
128
129print(classify_point((0, 0))) # Origin
130print(classify_point((5, 5))) # On diagonal at (5, 5)
131print(classify_point((3, 4))) # Quadrant I: (3, 4)
132
133# ============================================================
134# Dictionary dispatch pattern (alternative to if/elif chains)
135# ============================================================
136def add(a, b): return a + b
137def sub(a, b): return a - b
138def mul(a, b): return a * b
139def div(a, b): return a / b if b != 0 else "Error: division by zero"
140
141operations = {
142 "+": add,
143 "-": sub,
144 "*": mul,
145 "/": div,
146}
147
148op = "+"
149result = operations.get(op, lambda a, b: "Unknown op")(10, 3)
150print(f"10 {op} 3 = {result}") # 10 + 3 = 13

🏋️ Practice Exercise

Exercises:

  1. Write a function classify_bmi(bmi) that returns "underweight", "normal", "overweight", or "obese" based on BMI ranges. Use both if/elif and match-case versions.

  2. Implement a simple command parser using match-case that handles: "help", "quit", "say ", "move ", and unknown commands.

  3. Create a describe_type(value) function that uses match-case with type patterns to describe any Python value (e.g., "integer: 42", "empty list", "dict with 3 keys").

  4. Implement a dictionary dispatch calculator that supports +, -, *, /, **, and %. Handle division by zero gracefully.

  5. Write a function that takes a nested dict representing a JSON API response and uses match-case with mapping patterns to extract different fields based on the response type.

  6. Rewrite complex if/elif chains in your own code using match-case. Compare readability.

⚠️ Common Mistakes

  • Using if x == True: instead of if x:. The latter is more Pythonic and works with any truthy value. Only use is True when you specifically need to distinguish True from other truthy values.

  • Forgetting that Python has no switch fallthrough — each case block is independent. There's no need for break statements like in C/Java.

  • Using match-case as a simple switch when if/elif would be clearer for 2-3 conditions. Match-case shines with destructuring, not simple value comparisons.

  • Not understanding that case x: in match-case is a capture pattern (binds any value to x), not a comparison to a variable x. To compare to a variable, use a guard: case val if val == x:.

  • Writing empty if blocks. Use pass as a placeholder if you intentionally want to do nothing: if condition: pass.

💼 Interview Questions

🎤 Mock Interview

Practice a live interview for Conditionals & Pattern Matching