Python Conditionals: if/elif/else, Ternary Expressions, Match-Case (Python 3.10+), Truthy/Falsy Values, Short-Circuit Evaluation, and Decision Patterns
Every program makes decisions. Should this file be processed or skipped? Is this customer active or churned? Did the API return a success or an error? Should the pipeline retry or fail? Every one of these decisions is a conditional — code that runs only when a specific condition is true.
Conditionals are the traffic lights of programming. Green (True) — the code block runs. Red (False) — it is skipped. And just like traffic lights can have multiple signals (green arrow, flashing yellow, red with turn), Python has multiple conditional tools: if/elif/else for standard decisions, ternary expressions for one-liners, and the new match-case (Python 3.10+) for pattern matching.
This post covers conditionals from basic if statements to advanced patterns — including the truthy/falsy system that makes Python uniquely expressive, short-circuit evaluation that most tutorials skip, guard clauses that flatten your code, and match-case for clean multi-branch logic.
Table of Contents
- The if Statement
- Basic if
- if-else
- if-elif-else
- Indentation Matters
- Comparison Operators
- Equality and Inequality
- Greater Than, Less Than
- Chained Comparisons
- is vs == (Identity vs Equality)
- Logical Operators
- and, or, not
- Short-Circuit Evaluation
- Using Short-Circuit for Defaults
- Truthy and Falsy Values
- What Python Considers False
- Why This Matters
- Common Truthy/Falsy Patterns
- Ternary Expression (Conditional Expression)
- Basic Syntax
- Nested Ternary (and Why to Avoid It)
- Ternary in f-Strings
- Ternary in List Comprehensions
- Nested Conditionals vs Guard Clauses
- The Pyramid of Doom
- Guard Clauses (Early Return)
- Match-Case (Python 3.10+)
- Basic match-case
- Matching with Patterns
- Matching with Guards (if conditions)
- Matching Sequences and Dicts
- When to Use match-case vs if-elif
- Common Conditional Patterns
- Checking for None
- Checking for Empty Collections
- Safe Division
- Boundary Checking
- Data Validation Pattern
- Conditionals in Data Engineering
- Pipeline Branching
- Error Handling Decisions
- Data Quality Flags
- Common Mistakes
- Interview Questions
- Wrapping Up
The if Statement
Basic if
# The simplest conditional — run code only if condition is True
age = 25
if age >= 18:
print("You are an adult") # Runs because 25 >= 18 is True
# If the condition is False, the block is skipped entirely
temperature = 15
if temperature > 30:
print("It is hot outside") # Skipped — 15 > 30 is False
# Nothing happens. No error. The code just moves on.
Real-life analogy: if is like a security guard at a door. “If you have a badge, you may enter.” If you have a badge → you walk in. If not → you keep walking past the door. The guard does not chase you or throw an error — you simply do not enter.
if-else
# Two branches — one ALWAYS runs
age = 15
if age >= 18:
print("You are an adult")
else:
print("You are a minor") # This runs because 15 < 18
# Real-world: check API response
status_code = 404
if status_code == 200:
print("Success — process data")
else:
print(f"Error — status code: {status_code}") # Runs
if-elif-else
# Multiple branches — first True condition wins, rest are skipped
score = 85
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B" # This matches (85 >= 80) — stops here
elif score >= 70:
grade = "C" # Skipped — already matched above
elif score >= 60:
grade = "D" # Skipped
else:
grade = "F" # Skipped
print(f"Score: {score}, Grade: {grade}") # Score: 85, Grade: B
# KEY: Only ONE branch executes. Even if multiple conditions are True,
# Python picks the FIRST one and skips the rest.
# score = 95 → 95 >= 90 is True AND 95 >= 80 is True,
# but only the first match (A) runs.
# Real-world: categorize file sizes
file_size_mb = 250
if file_size_mb > 1000:
category = "large"
elif file_size_mb > 100:
category = "medium" # Matches
elif file_size_mb > 10:
category = "small"
else:
category = "tiny"
print(f"{file_size_mb} MB → {category}") # 250 MB → medium
Real-life analogy: if-elif-else is like a drive-through menu with escalating sizes. “Is it XL? No. Is it Large? No. Is it Medium? Yes — give them Medium. Do not check Small.” The cashier stops at the first match and does not continue asking.
Indentation Matters
# Python uses indentation (4 spaces) to define code blocks
# No curly braces {} like Java/C — indentation IS the structure
if True:
print("Inside the if block") # 4 spaces indent
print("Still inside") # Same indent = same block
print("Outside the if block") # No indent = runs always
# WRONG — inconsistent indentation causes IndentationError
# if True:
# print("line 1")
# print("line 2") # IndentationError: unexpected indent
# WRONG — missing indentation
# if True:
# print("oops") # IndentationError: expected an indented block
# Convention: use 4 SPACES (not tabs). Configure your editor to convert tabs to spaces.
Comparison Operators
Equality and Inequality
# == checks VALUE equality
print(5 == 5) # True
print(5 == 5.0) # True (int and float can be equal)
print("hello" == "hello") # True
print("Hello" == "hello") # False (case-sensitive!)
print([1, 2] == [1, 2]) # True (same contents)
# != checks inequality
print(5 != 3) # True
print("a" != "b") # True
print(5 != 5) # False
# COMMON BUG: using = instead of ==
# if x = 5: # SyntaxError! = is assignment, == is comparison
# if x == 5: # Correct
Greater Than, Less Than
print(10 > 5) # True
print(10 < 5) # False
print(10 >= 10) # True (greater than OR equal)
print(10 <= 9) # False
# Works with strings (alphabetical/lexicographic order)
print("apple" < "banana") # True (a comes before b)
print("apple" < "Apple") # False (lowercase > uppercase in ASCII)
# Works with dates
from datetime import date
print(date(2026, 6, 15) > date(2026, 1, 1)) # True
Chained Comparisons
# Python allows chaining — unique and powerful!
age = 25
if 18 <= age <= 65:
print("Working age") # True: 18 <= 25 AND 25 <= 65
# Equivalent to (but more readable than):
if age >= 18 and age <= 65:
print("Working age")
# Triple chain
x = 5
if 1 < x < 10:
print("x is between 1 and 10") # True
# Even this works (checks all adjacent pairs)
if 1 < 3 < 5 < 10:
print("All true") # 1<3 AND 3<5 AND 5<10 → True
# Data engineering: check if value is in expected range
row_count = 5000
if 0 < row_count < 1_000_000:
print("Row count is reasonable")
else:
print("WARNING: Row count is suspicious")
is vs == (Identity vs Equality)
# == checks VALUE: do these two have the same content?
# is checks IDENTITY: are these the exact same OBJECT in memory?
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True — same value (same contents)
print(a is b) # False — different objects (different memory addresses)
c = a
print(a is c) # True — same object (c is another label for a)
# RULES:
# Use == for comparing VALUES (almost always what you want)
# Use "is" ONLY for None, True, False
# ✅ Correct:
if result is None:
print("No result")
# ❌ Wrong (works by coincidence due to interning, but unreliable):
# if x is 42: # Might work for small ints, fails for large ones
# if name is "hello": # Might work due to string interning, but unreliable
# ✅ Correct:
if x == 42:
print("x is 42")
Logical Operators
and, or, not
# and — both must be True
age = 25
has_license = True
if age >= 18 and has_license:
print("Can drive") # Both True → runs
# or — at least one must be True
is_weekend = False
is_holiday = True
if is_weekend or is_holiday:
print("Day off!") # One is True → runs
# not — flips True/False
is_blocked = False
if not is_blocked:
print("Access granted") # not False = True → runs
# Combining all three
age = 25
is_student = True
is_senior = False
if (age >= 18 and is_student) or is_senior:
print("Eligible for discount")
# Precedence: not > and > or
# Use parentheses for clarity — never rely on memorizing precedence
Short-Circuit Evaluation
Python stops evaluating as soon as the result is determined. This is not just a performance optimization — it is a feature you can use intentionally.
# AND short-circuits: if the first is False, the second is NEVER evaluated
x = 0
if x != 0 and 10 / x > 2: # x is 0, so "x != 0" is False
print("Result") # 10/x is NEVER evaluated — no ZeroDivisionError!
# Without short-circuit, 10/0 would crash
# OR short-circuits: if the first is True, the second is NEVER evaluated
logged_in = True
if logged_in or check_credentials(): # logged_in is True
print("Welcome") # check_credentials() is NEVER called
# This is why ORDER MATTERS in conditions:
# Put the CHEAP/FAST check first, expensive check second
# ✅ if is_cached and fetch_from_database(): ← checks cache first
# ❌ if fetch_from_database() and is_cached: ← always hits database
Using Short-Circuit for Defaults
# Python's and/or return the ACTUAL VALUE, not just True/False
# This enables powerful one-liner defaults
# or returns the first truthy value (or the last value if all falsy)
name = "" or "Unknown"
print(name) # "Unknown" — "" is falsy, so or returns "Unknown"
name = "Naveen" or "Unknown"
print(name) # "Naveen" — "Naveen" is truthy, short-circuits
# Practical: default values
username = input_name or "Guest"
db_host = config.get("host") or "localhost"
page_size = user_pref or 25
# and returns the first falsy value (or the last value if all truthy)
result = "hello" and "world"
print(result) # "world" — both truthy, returns last
result = "" and "world"
print(result) # "" — first is falsy, short-circuits, returns ""
# Practical: safe attribute access
# name = user and user.name ← if user is None, returns None (not AttributeError)
Truthy and Falsy Values
What Python Considers False
In Python, EVERY value has a boolean interpretation. You do not need to write if x == True or if len(my_list) > 0. Python automatically evaluates values as True or False in boolean contexts.
| Falsy (evaluates to False) | Truthy (evaluates to True) |
|---|---|
False | True |
0, 0.0, 0j | Any non-zero number |
"" (empty string) | Any non-empty string (including "False"!) |
[] (empty list) | Any non-empty list |
() (empty tuple) | Any non-empty tuple |
{} (empty dict) | Any non-empty dict |
set() (empty set) | Any non-empty set |
None | Everything else |
# Proof
print(bool(0)) # False
print(bool(42)) # True
print(bool("")) # False
print(bool("hello")) # True
print(bool([])) # False
print(bool([1, 2])) # True
print(bool(None)) # False
print(bool("0")) # True! — non-empty string (even "0", "False", "None")
print(bool("False")) # True! — this trips everyone up
Why This Matters
# VERBOSE (unnecessary):
if len(my_list) > 0:
print("List has items")
if name != "" and name is not None:
print("Name exists")
if count == True:
print("Count is true")
# PYTHONIC (clean):
if my_list:
print("List has items")
if name:
print("Name exists")
if count:
print("Count is truthy")
Real-life analogy: Asking “Is the box empty?” is more natural than asking “Is the number of items in the box greater than zero?” Python’s truthy/falsy system lets you write code the natural way — if box instead of if len(box) > 0.
Common Truthy/Falsy Patterns
# Pattern 1: Check for data before processing
data = fetch_from_api()
if data: # Truthy = has content (not None, not [], not "")
process(data)
else:
print("No data received")
# Pattern 2: Provide defaults using or
name = user_input or "Guest" # "" → "Guest", "Naveen" → "Naveen"
page_size = params.get("limit") or 25 # None → 25, 0 → 25 (careful!)
# Pattern 3: Validate before using
results = query_database()
if not results: # Empty list or None
print("No results found")
return
# Pattern 4: Filter truthy values from a list
values = [0, 1, "", "hello", None, 42, [], [1, 2]]
truthy_only = [v for v in values if v]
print(truthy_only) # [1, 'hello', 42, [1, 2]]
# GOTCHA with "or" for defaults:
count = 0
result = count or 10 # 10! Because 0 is falsy
# If 0 is a valid value, use: result = count if count is not None else 10
Ternary Expression (Conditional Expression)
Basic Syntax
# Syntax: value_if_true IF condition ELSE value_if_false
# Standard if-else (4 lines):
if age >= 18:
status = "adult"
else:
status = "minor"
# Ternary (1 line — same result):
status = "adult" if age >= 18 else "minor"
# More examples:
label = "even" if x % 2 == 0 else "odd"
greeting = f"Good morning" if hour < 12 else "Good afternoon"
discount = 0.2 if is_member else 0.0
color = "red" if errors > 0 else "green"
# Data engineering:
load_type = "incremental" if has_watermark else "full"
file_format = "parquet" if size_mb > 100 else "csv"
Nested Ternary (and Why to Avoid It)
# You CAN nest ternaries — but SHOULD you?
grade = "A" if score >= 90 else "B" if score >= 80 else "C" if score >= 70 else "F"
# This works but is HARD TO READ. Compare with if-elif:
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
elif score >= 70:
grade = "C"
else:
grade = "F"
# RULE: Use ternary for simple two-way decisions.
# Use if-elif-else for three or more branches.
# Code is read 10x more than it is written — readability wins.
Ternary in f-Strings
# Embed conditionals directly in formatted strings
count = 1
print(f"Found {count} {'item' if count == 1 else 'items'}")
# "Found 1 item" — handles pluralization
count = 5
print(f"Found {count} {'item' if count == 1 else 'items'}")
# "Found 5 items"
# Status messages
status = "completed"
print(f"Pipeline {'succeeded' if status == 'completed' else 'failed'}!")
# "Pipeline succeeded!"
Ternary in List Comprehensions
# Transform values conditionally
nums = [1, -2, 3, -4, 5, -6]
absolute = [x if x >= 0 else -x for x in nums]
print(absolute) # [1, 2, 3, 4, 5, 6]
# Label values
labels = ["positive" if x > 0 else "negative" if x < 0 else "zero" for x in nums]
print(labels) # ['positive', 'negative', 'positive', 'negative', ...]
# Note the difference:
# [expr IF cond ELSE expr FOR x IN list] ← ternary in EXPRESSION (transforms every item)
# [expr FOR x IN list IF cond] ← filter (keeps only matching items)
Nested Conditionals vs Guard Clauses
The Pyramid of Doom
# BAD — deeply nested "pyramid of doom"
def process_order(order):
if order is not None:
if order.get("status") == "active":
if order.get("items"):
if order.get("payment"):
if order["payment"].get("verified"):
# Finally! The actual logic buried 5 levels deep
total = sum(item["price"] for item in order["items"])
return total
else:
return "Payment not verified"
else:
return "No payment info"
else:
return "No items"
else:
return "Order not active"
else:
return "No order"
# This is hard to read, hard to maintain, and a sign of poor structure
Guard Clauses (Early Return)
# GOOD — flat, readable guard clauses (early return)
def process_order(order):
if order is None:
return "No order"
if order.get("status") != "active":
return "Order not active"
if not order.get("items"):
return "No items"
if not order.get("payment"):
return "No payment info"
if not order["payment"].get("verified"):
return "Payment not verified"
# Happy path — clean and unindented
total = sum(item["price"] for item in order["items"])
return total
# Each guard clause handles ONE error and exits immediately
# The actual logic runs only if ALL guards pass
# No nesting, no pyramid, easy to add new checks
Real-life analogy: Guard clauses are like airport security. Each checkpoint handles one thing: “No ticket? Go back. No ID? Go back. Bag too heavy? Go back. Metal detected? Go back.” Only if you pass ALL checkpoints do you reach the gate. Each checkpoint is independent — not nested inside the previous one.
Match-Case (Python 3.10+)
match-case is Python’s answer to switch-case in other languages. But it is more powerful — it can match patterns, destructure sequences, and match types.
Basic match-case
# Replace if-elif chains with cleaner match-case
status_code = 404
match status_code:
case 200:
message = "OK — Success"
case 201:
message = "Created"
case 400:
message = "Bad Request"
case 401:
message = "Unauthorized"
case 403:
message = "Forbidden"
case 404:
message = "Not Found" # This matches
case 500:
message = "Internal Server Error"
case _: # Wildcard — matches anything (like else)
message = f"Unknown status: {status_code}"
print(message) # "Not Found"
# The underscore _ is the wildcard pattern — catches everything not matched above
# It MUST be the last case (like else in if-elif-else)
Matching with Patterns
# Match multiple values with | (OR pattern)
match day:
case "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday":
day_type = "Weekday"
case "Saturday" | "Sunday":
day_type = "Weekend"
case _:
day_type = "Invalid day"
# Match and CAPTURE values
command = ("move", 10, 20)
match command:
case ("quit",):
print("Quitting")
case ("move", x, y): # Captures x=10, y=20
print(f"Moving to ({x}, {y})")
case ("shoot", direction):
print(f"Shooting {direction}")
case _:
print("Unknown command")
Matching with Guards (if conditions)
# Add conditions to case blocks using "if" (guards)
age = 25
match age:
case n if n < 0:
category = "Invalid"
case n if n < 13:
category = "Child"
case n if n < 18:
category = "Teenager"
case n if n < 65:
category = "Adult" # Matches: 25 < 65
case _:
category = "Senior"
print(f"Age {age}: {category}") # Age 25: Adult
# Data engineering: categorize file processing results
match (rows_read, rows_written, error_count):
case (0, 0, 0):
status = "Empty source — nothing to process"
case (n, m, 0) if n == m:
status = f"Success — {n} rows loaded"
case (n, m, 0) if n > m:
status = f"Warning — {n - m} rows filtered out"
case (_, _, e) if e > 0:
status = f"Errors — {e} rows failed"
case _:
status = "Unknown result"
Matching Sequences and Dicts
# Match list patterns
match data:
case []:
print("Empty list")
case [single]:
print(f"One item: {single}")
case [first, second]:
print(f"Two items: {first}, {second}")
case [first, *rest]:
print(f"First: {first}, remaining: {rest}")
# Match dict patterns (Python 3.10+)
match event:
case {"type": "login", "user": username}:
print(f"{username} logged in")
case {"type": "purchase", "amount": amount} if amount > 100:
print(f"Large purchase: ${amount}")
case {"type": "error", "message": msg}:
print(f"Error: {msg}")
case _:
print("Unknown event")
When to Use match-case vs if-elif
| Scenario | Use if-elif | Use match-case |
|---|---|---|
| 2-3 conditions | Yes (simpler) | Overkill |
| Comparing against many fixed values | Verbose | Yes (cleaner) |
| Range checks (x > 10, x < 50) | Yes (natural) | Possible with guards but less natural |
| Destructuring sequences/dicts | Messy | Yes (designed for this) |
| Status codes, command parsing | Okay | Yes (ideal use case) |
| Need Python 3.9 compatibility | Yes (only option) | Not available |
Common Conditional Patterns
Checking for None
# Always use "is None" / "is not None" — never == None
result = fetch_data()
# ✅ Correct:
if result is None:
print("No data")
if result is not None:
process(result)
# ❌ Wrong:
# if result == None: # Works but violates PEP 8 and can be overridden
# if not result: # Catches None BUT ALSO [], "", 0 — too broad!
Checking for Empty Collections
# ✅ Pythonic — use truthy/falsy
if my_list:
print("Has items")
if not my_dict:
print("Empty dict")
# ❌ Unpythonic
# if len(my_list) > 0: # Works but verbose
# if my_list != []: # Works but ugly
# if len(my_list) != 0: # Just... no
Safe Division
# Guard against ZeroDivisionError
total = 100
count = 0
# Pattern 1: if check
average = total / count if count != 0 else 0
# Pattern 2: short-circuit
average = count and total / count # 0 (count is falsy, returns 0, skips division)
# Pattern 3: try/except (for unexpected zeros)
try:
average = total / count
except ZeroDivisionError:
average = 0
Boundary Checking
# Check if a value is within expected boundaries
def validate_age(age):
if not isinstance(age, (int, float)):
return False, "Age must be a number"
if age < 0:
return False, "Age cannot be negative"
if age > 150:
return False, "Age seems unrealistic"
return True, "Valid"
valid, message = validate_age(25)
print(f"{valid}: {message}") # True: Valid
valid, message = validate_age(-5)
print(f"{valid}: {message}") # False: Age cannot be negative
Data Validation Pattern
# Validate a data record using guard clauses
def validate_customer(record):
errors = []
if not record.get("customer_id"):
errors.append("Missing customer_id")
if not record.get("email"):
errors.append("Missing email")
elif "@" not in record["email"]:
errors.append("Invalid email format")
if record.get("age") is not None:
if not isinstance(record["age"], (int, float)):
errors.append("Age must be numeric")
elif record["age"] < 0 or record["age"] > 150:
errors.append("Age out of range")
return {"valid": len(errors) == 0, "errors": errors}
# Usage
result = validate_customer({"customer_id": 1001, "email": "test", "age": -5})
print(result)
# {'valid': False, 'errors': ['Invalid email format', 'Age out of range']}
Conditionals in Data Engineering
Pipeline Branching
# Decide processing strategy based on data characteristics
row_count = get_source_row_count()
last_load = get_last_load_timestamp()
if row_count == 0:
print("Source is empty — skipping pipeline")
elif last_load is None:
print("First run — performing full load")
run_full_load()
elif row_count > 10_000_000:
print("Large dataset — using parallel load")
run_parallel_incremental_load()
else:
print("Standard incremental load")
run_incremental_load(since=last_load)
Error Handling Decisions
# Decide what to do based on error type
def handle_pipeline_error(error_type, table_name, retry_count):
if error_type == "timeout" and retry_count < 3:
print(f"Retrying {table_name} (attempt {retry_count + 1})")
return "retry"
elif error_type == "auth_expired":
print("Refreshing authentication token")
return "refresh_and_retry"
elif error_type == "source_unavailable":
print(f"Source down — skipping {table_name}, will retry next run")
return "skip"
else:
print(f"Fatal error on {table_name}: {error_type}")
return "fail"
Data Quality Flags
# Flag data quality issues using conditionals
def add_quality_flags(record):
flags = []
if record.get("email") and "@" not in record["email"]:
flags.append("invalid_email")
if record.get("age") is not None and (record["age"] < 0 or record["age"] > 120):
flags.append("suspicious_age")
if record.get("city") and record["city"].isdigit():
flags.append("numeric_city")
if not record.get("customer_id"):
flags.append("missing_id")
record["quality_flags"] = flags
record["is_clean"] = len(flags) == 0
return record
Common Mistakes
- Using
=instead of==—if x = 5is assignment (SyntaxError).if x == 5is comparison. Python catches this as a syntax error, but it is a common typo. - Using
==to check for None — useis Noneandis not None.==can be overridden by custom classes to return True for non-None values.ischecks identity and cannot be overridden. - Using
if not xwhen you meanif x is None—not xis True for None, 0, “”, [], {} — all falsy values. If 0 or “” are valid values in your context,not xis too broad. Usex is Nonefor explicit None checks. - Forgetting that
bool("False")is True — the string"False"is non-empty, so it is truthy. If you receive “true”/”false” strings from a config or API, usevalue.lower() == "true", notbool(value). - Deep nesting instead of guard clauses — if your code has more than 3 levels of nesting, refactor using guard clauses (early return). Flat code is easier to read, test, and maintain.
- Using
orfor defaults when 0 is valid —count = user_count or 10gives 10 when user_count is 0 (because 0 is falsy). If 0 is a valid value, usecount = user_count if user_count is not None else 10. - Not using chained comparisons — writing
if x >= 0 and x <= 100instead ofif 0 <= x <= 100. Chaining is more readable and more Pythonic.
Interview Questions
Q: What are truthy and falsy values in Python?
A: Every Python value has a boolean interpretation. Falsy values (evaluate to False): False, 0, 0.0, "", [], (), {}, set(), None. Everything else is truthy. This is why if my_list: works — an empty list is falsy, a non-empty list is truthy.
Q: What is short-circuit evaluation?
A: Python stops evaluating boolean expressions as soon as the result is determined. For and, if the first operand is False, the second is never evaluated. For or, if the first is True, the second is never evaluated. This prevents errors (e.g., x != 0 and 10/x avoids division by zero) and enables defaults (e.g., name or "Guest").
Q: What is the ternary expression in Python?
A: value_if_true if condition else value_if_false — a one-line conditional assignment. Example: status = "adult" if age >= 18 else "minor". Use for simple two-way decisions. Avoid nesting ternaries — use if-elif-else for 3+ branches.
Q: Why use is None instead of == None?
A: is checks identity (same object in memory) and cannot be overridden. == checks equality and CAN be overridden by custom classes via __eq__. Since None is a singleton (only one None object exists), is None is both correct and faster.
Q: What are guard clauses and why are they better than nested ifs? A: Guard clauses check for invalid conditions at the start of a function and return immediately. Instead of nesting 5 levels deep, each guard handles one error case and exits. The “happy path” (actual logic) runs only if all guards pass, and it stays at the top indentation level — flat, readable, and easy to maintain.
Q: What is match-case in Python and when would you use it?
A: Introduced in Python 3.10, match-case is a structural pattern matching statement. It matches values against patterns — constants, sequences, dicts, types, or even captured variables. Use it for HTTP status code handling, command parsing, event processing, and any scenario with many discrete cases. It is cleaner than long if-elif chains for matching against fixed values.
Wrapping Up
Conditionals are the decision-making backbone of every program. Master if/elif/else for standard branching, ternary expressions for clean one-liners, and match-case for multi-value pattern matching. Understand truthy/falsy values to write Pythonic code (if my_list instead of if len(my_list) > 0). Use short-circuit evaluation for safe access and defaults. And flatten your code with guard clauses instead of building pyramids.
The mark of an experienced Python developer is not just knowing the syntax — it is knowing when to use which conditional pattern and keeping the code flat, readable, and intentional.
Previous in this series: Dictionaries — Nesting, Comprehensions, and DefaultDict
Next in this series: Loops — for, while, enumerate, zip, break/continue
Naveen Vuppula is a Senior Data Engineering Consultant and app developer based in Ontario, Canada. He writes about Python, SQL, AWS, Azure, and everything data engineering at DriveDataScience.com.