🌱 Beginner

Control Flow (if / elif / else)

📖 Lesson 09 ⏱ 25 min 🧪 5 questions 💻 3 exercises

🎯 Learning Objectives

  • Use if, elif, and else to make decisions in code
  • Understand how Python uses indentation to define blocks
  • Write nested conditionals and know when to avoid them
  • Use the ternary (conditional) expression for concise one-liners
  • Apply match/case for structural pattern matching (Python 3.10+)
  • Follow best practices for clean, readable conditional logic

The if Statement

The if statement lets your program make decisions. Code inside the if block runs only when the condition is True.

age = 20

if age >= 18:
    print("You are an adult.")
    print("You can vote.")

print("This always runs — it's outside the if block.")
basic_if.py
Python uses indentation (4 spaces by convention) to define code blocks. There are no curly braces {}. The indented lines after the : are the body of the if. When indentation returns to the previous level, the block is over.

if / else

else provides an alternative block that runs when the condition is False:

temperature = 35

if temperature > 30:
    print("It's hot! Stay hydrated.")
else:
    print("Temperature is comfortable.")
if_else.py

if / elif / else

elif (short for "else if") lets you check multiple conditions in sequence. Python evaluates them top to bottom and runs the first block whose condition is True:

score = 85

if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
elif score >= 70:
    grade = "C"
elif score >= 60:
    grade = "D"
else:
    grade = "F"

print(f"Score: {score} → Grade: {grade}")  # Score: 85 → Grade: B
elif.py
Only one branch in an if/elif/else chain executes. Once a condition matches, all remaining branches are skipped — even if their conditions would also be True.

Key rules

  • if is required — it starts the chain.
  • elif is optional — use as many as needed (0 or more).
  • else is optional — it's the catch-all for when nothing else matched.
  • Each block must contain at least one statement. Use pass as a no-op placeholder.
# pass as a placeholder
if condition:
    pass  # TODO: implement this later
else:
    handle_other_case()
pass_placeholder.py

Nested Conditionals

You can put if statements inside other if statements:

age = 25
has_id = True

if age >= 18:
    if has_id:
        print("Entry allowed.")
    else:
        print("Please show your ID.")
else:
    print("You must be 18 or older.")
nested.py
Deep nesting makes code hard to read. Prefer flat structures by combining conditions or using early returns/guard clauses.

Flattening with combined conditions

# Instead of nesting:
if age >= 18 and has_id:
    print("Entry allowed.")
elif age >= 18:
    print("Please show your ID.")
else:
    print("You must be 18 or older.")
flat.py

Ternary (Conditional) Expression

Python's one-line conditional expression — useful for simple assignments:

# Syntax: value_if_true if condition else value_if_false

age = 20
status = "adult" if age >= 18 else "minor"
print(status)  # "adult"

# Use in f-strings
print(f"You are {'eligible' if age >= 18 else 'not eligible'}.")

# Use in function calls
print(max(a, b) if a != b else "equal")

# Don't overuse — keep it simple
# BAD: nested ternary (hard to read)
# result = "A" if x > 90 else "B" if x > 80 else "C"
# Better: use if/elif/else for complex logic
ternary.py
Use the ternary expression for simple, one-condition assignments. If you need multiple conditions or the expression gets long, switch to a full if/elif/else block.

Structural Pattern Matching (match / case)

Python 3.10 introduced match/case — a powerful alternative to long if/elif chains when checking a value against multiple patterns:

command = "quit"

match command:
    case "start":
        print("Starting...")
    case "stop":
        print("Stopping...")
    case "quit" | "exit":   # multiple patterns with |
        print("Goodbye!")
    case _:                  # _ is the wildcard (like else)
        print(f"Unknown command: {command}")
match_basic.py

Pattern matching with structure

# Match on data structure shape
def handle_point(point):
    match point:
        case (0, 0):
            print("Origin")
        case (x, 0):
            print(f"On X-axis at x={x}")
        case (0, y):
            print(f"On Y-axis at y={y}")
        case (x, y):
            print(f"Point at ({x}, {y})")

handle_point((3, 0))   # On X-axis at x=3
handle_point((0, 5))   # On Y-axis at y=5
handle_point((2, 7))   # Point at (2, 7)

# Guard conditions with 'if'
def classify_age(age):
    match age:
        case n if n < 0:
            return "Invalid"
        case n if n < 13:
            return "Child"
        case n if n < 18:
            return "Teenager"
        case _:
            return "Adult"
match_advanced.py
match/case is more than a switch statement — it can destructure tuples, lists, dicts, and even class instances. We'll see more advanced patterns when we cover classes (Lesson 20).

Guard Clauses (Early Return)

A guard clause handles edge cases or invalid inputs at the top of a function, returning early. This keeps the main logic flat and readable:

# Without guard clauses — deeply nested
def process_order(order):
    if order is not None:
        if order["status"] == "active":
            if order["total"] > 0:
                # actual logic here
                ship(order)
            else:
                raise ValueError("Empty order")
        else:
            raise ValueError("Inactive order")
    else:
        raise ValueError("No order")

# With guard clauses — flat and clear
def process_order(order):
    if order is None:
        raise ValueError("No order")
    if order["status"] != "active":
        raise ValueError("Inactive order")
    if order["total"] <= 0:
        raise ValueError("Empty order")

    # Main logic — no nesting!
    ship(order)
guard_clauses.py
Guard clauses are a professional pattern used in production code everywhere. The rule: handle the exceptional/invalid cases first, then write the happy path at the lowest indentation level.

Best Practices

# 1. Use truthiness directly
items = []
if not items:          # ✓ Pythonic
    print("Empty")
# if len(items) == 0:  # ✗ Verbose

# 2. Avoid comparing to True/False explicitly
is_valid = True
if is_valid:           # ✓
    process()
# if is_valid == True: # ✗

# 3. Use 'is' for None checks
if result is None:     # ✓
    handle_none()
# if result == None:   # ✗

# 4. Keep conditions simple — extract to variables
# Hard to read:
if user.age >= 18 and user.is_verified and not user.is_banned and user.balance > 0:
    allow()

# Better:
is_eligible = user.age >= 18 and user.is_verified
is_active = not user.is_banned and user.balance > 0
if is_eligible and is_active:
    allow()

# 5. Prefer positive conditions
if is_valid:     # ✓ easier to understand
    do_thing()
else:
    handle_error()

# Avoid double negatives:
# if not is_invalid:  # ✗ confusing
best_practices.py
🤖

Ask your AI tutor! Not sure when to use match/case vs if/elif? Want help refactoring nested conditionals into guard clauses? Control flow is where programs become interesting — ask anything.

💻 Exercises

01 Grade Calculator

Write a script that takes a numeric score (0–100) and prints the letter grade: A (90–100), B (80–89), C (70–79), D (60–69), F (below 60). Also handle invalid scores (negative or above 100) with an error message.

Show solution
score = 85

if score < 0 or score > 100:
    print("Invalid score!")
elif score >= 90:
    print("A")
elif score >= 80:
    print("B")
elif score >= 70:
    print("C")
elif score >= 60:
    print("D")
else:
    print("F")
02 Ticket Pricer

Write a ticket pricing system. Base price is $10. Apply these rules in order: children (under 12) get 50% off, seniors (65+) get 40% off, students get 20% off (pass a boolean), weekends add a $2 surcharge (pass a boolean). Print the final price.

Show solution
age = 70
is_student = False
is_weekend = True
price = 10.00

if age < 12:
    price *= 0.5
elif age >= 65:
    price *= 0.6
elif is_student:
    price *= 0.8

if is_weekend:
    price += 2.00

print(f"Ticket price: ${price:.2f}")
# Ticket price: $8.00 (senior 40% off = $6, + $2 weekend)
03 FizzBuzz (Single Number)

Write code that takes a number n and prints: "FizzBuzz" if divisible by both 3 and 5, "Fizz" if divisible by 3 only, "Buzz" if divisible by 5 only, or the number itself otherwise. Test with n = 15, 9, 10, and 7.

Show solution
for n in [15, 9, 10, 7]:
    if n % 3 == 0 and n % 5 == 0:
        print("FizzBuzz")
    elif n % 3 == 0:
        print("Fizz")
    elif n % 5 == 0:
        print("Buzz")
    else:
        print(n)
# FizzBuzz
# Fizz
# Buzz
# 7