In the previous lesson, you learned Python's core data types — strings, integers, floats, booleans, and None. Now it's time to make your programs actually think.
Imagine you're baking a cake. You check the oven temperature, decide if the batter is mixed enough, and choose the right icing. Every step is a decision.
In Python, control flow is exactly that — a way to tell your code how to make decisions and choose what to do next. Today, we'll master two superhero tools: if…else and match.
By the end, you'll write code that thinks, chooses, and acts — just like you do!
- How
if,elif, andelselet your code make decisions - Nested if statements for hierarchical logic
- Python's modern
matchstatement (Python 3.10+) - Advanced match features: pattern unpacking and guard clauses
- When to use
if...elifversusmatch - A complete hands-on project: a Food Order Billing System
- 1. What is Control Flow? The Traffic Cop of Your Code
- 2. Python If…Else: Your First Decision-Maker
- 3. Real-World Example: Smart Home Thermostat
- 4. Nested If Statements: Decisions Within Decisions
- 5. Introducing Python Match (Python 3.10+)
- 6. Level Up: Advanced Match Features
- 7. Final Project: The Ultimate Food Order System
- 8. Quick Comparison & When to Use What
- 9. Quick Reference Cheat Sheet
- 10. Common Pitfalls (And How to Avoid Them)
- 11. Practice Exercises
- 12. FAQ
- 13. Summary
🚦 1. What is Control Flow? The Traffic Cop of Your Code
Control flow is the order in which your program runs its instructions. Without it, code runs top-to-bottom, like a robot with no brain.
With control flow, your code becomes smart. It can react, decide, and adapt.
Each page asks: "Go left?" or "Fight the dragon?" Your choice changes the story. Python's
if and match are your way of writing those choices into code.
🔀 2. Python If…Else: Your First Decision-Maker
The if statement is the most basic form of decision-making. It checks a condition: if it's true, it runs a block of code. If not, it skips it.
The Simple "If": A One-Way Check
Let's start with checking if a student passed an exam.
score to passing_marks. Since 75 is greater than 40, the condition is True, so the indented print() line underneath runs.
score = 75
passing_marks = 40
if score > passing_marks:
print("Congratulations! You passed the exam!")
Output:
Congratulations! You passed the exam!
See what happened? The condition (score > passing_marks) was True (75 is greater than 40), so Python ran the indented print statement.
In Python, the spaces or tabs at the start of a line define a code block. After the
if line, you must indent the next line. Usually, we use 4 spaces. Your editor will often do this for you!
Adding "Else": The Other Option
What if the student failed? We need a backup plan. That's where else comes in.
score (35) is not greater than passing_marks (40), so the if condition is False and Python runs the else block instead.
score = 35
passing_marks = 40
if score > passing_marks:
print("Congratulations! You passed the exam!")
else:
print("Sorry, you did not pass. Try again next time.")
Output:
Sorry, you did not pass. Try again next time.
Now our program handles both possibilities. It's a simple but powerful fork in the road.
Introducing "Elif": The Middle Ground
Life isn't just pass/fail. What about grades like A, B, C? We need more branches. Enter elif (short for "else if").
score against five ranges, top to bottom. Since 82 fails the first check (>= 90) but passes the second (>= 75), Python runs that block and skips every condition below it.
score = 82
if score >= 90:
print("Grade: A - Outstanding!")
elif score >= 75:
print("Grade: B - Great job!")
elif score >= 60:
print("Grade: C - Good effort.")
elif score >= 40:
print("Grade: D - You passed, but aim higher.")
else:
print("Grade: F - Please retake the exam.")
Output:
Grade: B - Great job!
Python checks each condition in order. The first one that is True wins, and it runs that block. The rest are ignored.
Always write your conditions from most specific to least specific. If you write
if score >= 40: first, it will be True for a score of 95, and the "A" grade block will never run!
🌡️ 3. Real-World Example: Smart Home Thermostat
Let's build a practical program. Imagine a thermostat that decides whether to turn the heater, AC, or fan on.
current_temp = 22 # in Celsius
ideal_min = 20
ideal_max = 24
print(f"Current room temperature: {current_temp}°C")
if current_temp < ideal_min:
action = "Turning HEATER ON."
reason = f"Too cold (below {ideal_min}°C)."
elif current_temp > ideal_max:
action = "Turning AC ON."
reason = f"Too hot (above {ideal_max}°C)."
else:
action = "Turning FAN ON for circulation."
reason = "Temperature is comfortable."
print(f"Decision: {action}")
print(f"Reason: {reason}")
Output:
Current room temperature: 22°C
Decision: Turning FAN ON for circulation.
Reason: Temperature is comfortable.
This is a clear, multi-step decision tree. Your code is now making logical choices like a real appliance!
🪆 4. Nested If Statements: Decisions Within Decisions
Sometimes you need to check a second condition only after the first is true. This is called nesting.
Example: A movie ticket booking system that checks age, then applies a student discount.
age is 19, that's False, so Python moves into the else branch — where a second, nested if checks student status and applies a 25% discount.
age = 19
is_student = True
ticket_price = 12.50
if age < 18:
ticket_price = ticket_price * 0.5 # 50% child discount
print(f"Child discount applied. Ticket price: ${ticket_price}")
else:
# This "else" block is for adults (age >=18)
if is_student:
ticket_price = ticket_price * 0.75 # 25% student discount
print(f"Student discount applied. Ticket price: ${ticket_price}")
else:
print(f"Standard adult price: ${ticket_price}")
Output:
Student discount applied. Ticket price: $9.375
The second if statement is nested inside the first else block. It only runs for adults.
Think "Is the person a child? If not, are they a student?" This step-by-step logic keeps your code clean and mirrors real-world decision-making.
🎯 5. Introducing Python Match (Python 3.10+): The Super-Powered Switch
The match statement (sometimes called structural pattern matching) is a newer, cleaner way to handle multiple, specific conditions. It's like a super-charged version of if...elif...elif for when you're checking the value of a single variable.
match requires Python 3.10 or newer. Run python3 --version in your terminal first — if you're on an older version, stick with if...elif chains (they work identically, just more verbosely) until you can upgrade.
Why Use Match? The "Weekday Planner" Example
Imagine planning your day based on the weekday. With if...elif, it works but can be clunky.
day against each weekday name one at a time using repeated == comparisons, landing on the "Team meeting day" plan for Tuesday.
# The 'if...elif' way
day = "Tuesday"
if day == "Monday":
plan = "Start new work projects."
elif day == "Tuesday":
plan = "Team meeting day."
elif day == "Wednesday":
plan = "Mid-week review."
elif day == "Thursday":
plan = "Client calls."
elif day == "Friday":
plan = "Wrap up work, plan weekend."
else:
plan = "Relax! It's the weekend."
print(f"Today is {day}. Plan: {plan}")
This is repetitive—we keep writing day ==. The match statement simplifies this pattern beautifully.
The Match Statement: Clean and Readable
Here's the same logic with match:
match/case. Python compares day against each case value top to bottom and runs the first one that matches — case _ is the catch-all if nothing else fits.
day = "Tuesday"
match day:
case "Monday":
plan = "Start new work projects."
case "Tuesday":
plan = "Team meeting day."
case "Wednesday":
plan = "Mid-week review."
case "Thursday":
plan = "Client calls."
case "Friday":
plan = "Wrap up work, plan weekend."
case _: # The underscore `_` is the "catch-all" default case
plan = "Relax! It's the weekend."
print(f"Today is {day}. Plan: {plan}")
Output:
Today is Tuesday. Plan: Team meeting day.
Isn't that cleaner? No more repeating the variable name. It's like telling Python: "Here's the value. Now, find which case it matches and run that code."
The underscore
_ in a match statement acts exactly like the final else:. It catches any value that didn't match the previous cases. Always include it to handle unexpected inputs gracefully.
⚡ 6. Level Up: Advanced Match Features
Match is not just for simple values. It can unpack data and match patterns, which is incredibly powerful.
Matching with Lists (Pattern Unpacking)
Let's say you have a list representing a command for a robot: ["move", "north", 10] means "move north 10 units". We can use match to understand this command.
command list, not just its value. Because it has exactly 3 items starting with "move", Python unpacks the 2nd and 3rd items directly into the new variables direction and distance for you to use immediately.
command = ["move", "east", 5]
match command:
case ["move", direction, distance]:
print(f"Robot moving {distance} units to the {direction}.")
case ["rotate", degrees]:
print(f"Robot rotating by {degrees} degrees.")
case ["stop"]:
print("Robot stopping.")
case _:
print("Error: Command not understood.")
Output:
Robot moving 5 units to the east.
Magic! The match statement didn't just check the value; it unpacked the list into variables (direction and distance) that we could use directly.
Matching with Conditions (Guard Clauses)
You can add an if condition inside a case to make it more specific. This is called a "guard".
Example: Processing user input with validation.
user_input tuple into username/password, then checks each case's guard (the if part) in order — the first guard whose pattern and condition are both true wins.
user_input = ("admin", "password123")
match user_input:
case (username, password) if username == "admin" and password == "secret":
print("Access granted to super-admin panel.")
case (username, password) if username == "admin":
print("Access granted to standard admin panel.")
case (_, _):
print("Access denied. Invalid credentials.")
Output:
Access granted to standard admin panel.
"Access granted to standard admin panel.", not "Access denied." as sometimes claimed. Here's why it matters: the first guard fails because the password is "password123", not "secret" — but the second guard only checks the username, not the password at all. So any password paired with "admin" silently falls through to standard admin access. This is a textbook example of a security bug caused by an incomplete guard clause — always make sure every guard in a sensitive check re-validates every field, not just the ones that changed.
The guard (if username == "admin") adds an extra check after the pattern matches. It makes your matching logic incredibly precise — but as the example above shows, it's only as safe as the conditions you actually write.
Pattern matching is powerful, but for simple "value equals X" checks, a simple
if...elif or a dictionary might be clearer. Use match when you truly need its pattern-unpacking superpowers.
🍕 7. Final Project: The Ultimate Food Order System
Let's combine everything into a mini-project. We'll build a system that takes an order and calculates the bill with discounts and combos.
match to look up the base menu price for an (item, size) pair, then uses plain if statements to layer on a takeaway fee and a loyalty discount, finishing with a formatted receipt — showing match and if working together, each doing what it's best at.
# The Ultimate Food Order System
item = "Pizza"
size = "Large"
is_takeaway = True
loyalty_member = False
base_price = 0
# Step 1: Set base price with MATCH (perfect for specific values)
match (item, size):
case ("Burger", "Regular"):
base_price = 5.99
case ("Burger", "Large"):
base_price = 8.99
case ("Pizza", "Regular"):
base_price = 10.99
case ("Pizza", "Large"):
base_price = 15.99
case ("Salad", _): # Any size salad is same price
base_price = 7.49
case _:
print("Item/size not on menu.")
base_price = 0
# Step 2: Apply modifiers with IF...ELIF
final_price = base_price
if is_takeaway:
# Add a packaging fee
final_price += 1.50
print("Added takeaway packaging fee: $1.50")
if loyalty_member and base_price > 0:
# Apply a 10% discount
discount = base_price * 0.10
final_price -= discount
print(f"Loyalty discount applied: -${discount:.2f}")
# Step 3: Final output
if base_price > 0:
print("=" * 30)
print(f"ORDER SUMMARY:")
print(f"Item: {size} {item}")
print(f"Base Price: ${base_price:.2f}")
print(f"Total Due: ${final_price:.2f}")
else:
print("Cannot process order.")
Output:
Added takeaway packaging fee: $1.50
==============================
ORDER SUMMARY:
Item: Large Pizza
Base Price: $15.99
Total Due: $17.49
This project shows the perfect team: match elegantly handles the menu lookup (matching specific pairs), while if statements handle the conditional add-ons and discounts.
loyalty_member = True test run yourself, or add a new menu item — since base_price > 0 gates the loyalty discount, you'll see firsthand why that guard exists (it stops a discount being calculated on an unrecognized $0 order).
⚖️ 8. Quick Comparison & When to Use What
- Use
if...elif...elsewhen:- Conditions are based on ranges (
score > 90). - You need to check multiple, unrelated variables.
- Your logic is complex with many
and/oroperators.
- Conditions are based on ranges (
- Use
matchwhen:- You are checking the exact value of a single variable.
- You have many specific cases (like days of the week, menu items).
- You want to unpack data from lists or tuples as part of the check.
- You're using Python 3.10 or newer.
The best control flow tool is the one that makes your intention crystal clear to the next person reading your code (who might be you in 6 months!). Readability is a superpower.
📚 9. Quick Reference Cheat Sheet
Basic if / elif / else:
if condition:
pass # runs if True
elif other_condition:
pass # checked only if first is False
else:
pass # runs if nothing above matched
Nesting:
if outer_condition:
if inner_condition:
pass # runs only if BOTH are True
Match / case (Python 3.10+):
match value:
case "a":
pass
case "b" | "c": # OR pattern: matches "b" OR "c"
pass
case [x, y]: # unpacks a 2-item list/tuple
pass
case (x, y) if x > 0: # guard clause
pass
case _:
pass # catch-all, always include this
🚧 10. Common Pitfalls (And How to Avoid Them)
# Bad — this ALWAYS matches first for any score >= 40, so
# an "A" score of 95 gets mislabeled as a "D"!
if score >= 40:
print("D")
elif score >= 90:
print("A") # unreachable!
# Good — most specific / highest threshold checked first
if score >= 90:
print("A")
elif score >= 40:
print("D")
❌ Pitfall 2: Forgetting the catch-all case `_` in match
# Risky — if day is something unexpected (e.g. a typo),
# nothing runs and 'plan' is never defined -> NameError later
match day:
case "Monday":
plan = "Work"
case "Tuesday":
plan = "Meetings"
# Safe — always handle the unexpected
match day:
case "Monday":
plan = "Work"
case "Tuesday":
plan = "Meetings"
case _:
plan = "Unknown day"
❌ Pitfall 3: A guard clause that doesn't check everything it implies
# Bad — this guard implies "verified admin" but only checks username
case (username, password) if username == "admin":
print("Access granted") # password is never actually checked here!
# Good — every guard on a sensitive path re-validates every field
case (username, password) if username == "admin" and password == correct_password:
print("Access granted")
❌ Pitfall 4: Using match on Python older than 3.10
# This raises a SyntaxError on Python 3.9 or earlier
match day:
case "Monday":
pass
# Check your version first:
# python3 --version
🏋️ 11. Practice Exercises
Write an
if...elif...else chain that takes a traffic light color ("red", "yellow", "green") and prints "Stop", "Slow down", or "Go".
Rewrite Exercise 1 using
match...case instead. Add a case _: that handles an invalid color.
Extend the movie ticket example: add a senior discount (age ≥ 65 gets 40% off, checked before the student discount) using nested
if statements.
Using
match and pattern unpacking, write a parser for commands like ["deposit", 100] and ["withdraw", 50]. Add a guard clause that rejects withdrawals greater than a fixed balance.
Take the guard-clause login example from this post and fix it so a real password check is enforced on every case that grants access — not just the super-admin case.
❓ 12. Frequently Asked Questions
What is control flow in Python?
Control flow is the order in which a program's statements execute. Tools like if, elif, else, and match let a program branch — running different code depending on a condition — instead of always executing top to bottom.
What Python version do I need for the match statement?
match...case requires Python 3.10 or later. Check your version with python3 --version; on earlier versions, use an if...elif...else chain instead — the logic is equivalent.
Is Python's match statement the same as a switch statement in other languages?
It's similar in spirit but more powerful. Unlike a traditional switch, Python's match can unpack data structures (like lists and tuples) directly into variables and supports guard clauses (extra if conditions) on each case.
When should I use if...elif instead of match?
Use if...elif when your conditions are ranges (score > 90), involve multiple unrelated variables, or rely on complex and/or logic. Use match when you're checking one variable against several specific, exact values — or when you want to unpack a structure like a list or tuple as part of the check.
What does the underscore _ mean in a match statement?
It's the wildcard/catch-all pattern — equivalent to a final else:. It matches anything that didn't match an earlier case, and you should always include one so unexpected input doesn't silently fall through with no branch executed.
What is a guard clause in Python pattern matching?
A guard is an if condition attached to a case, written as case pattern if condition:. The case only matches if both the structural pattern and the guard condition are true — but as shown in this post's login example, a guard that doesn't check every relevant value can silently create a bug.
📝 13. Summary: You Are Now a Control Flow Master! 🏆
You've learned how to make your code think:
if: Makes a simple yes/no decision.if...else: Chooses between two clear paths.if...elif...else: Navigates a multi-branch decision tree.match...case: Elegantly matches a value against many specific options and can unpack data.- Guard clauses: add extra precision to
match— but must check every relevant condition, not just the first one that changed.
Ready for the next step? Continue to Python Functions, where you'll package this decision-making logic into clean, reusable blocks of code.
Happy coding! ✨
Comments
Post a Comment