Control Flow: If / Elif / Else

Reviewed & published by Brayan K

Learn how to make your programs smart by having them make decisions based on conditions.

Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

What is Control Flow?

Control flow is how your program decides what code to run based on conditions. Without it, programs would just run line by line, doing the same thing every time.

With control flow, your program can:

Think of control flow like a traffic light. The light checks conditions (time, sensors) and decides whether to show green, yellow, or red. Your program works the same way!

🧠 How Python Reads Conditions: The Mental Model

Before writing a single line of control flow, build this mental model — it explains every behaviour you'll encounter.

Rule 1: Python reads conditions top to bottom

It checks the first if, then each elif in order. The moment one is True, it runs that block and skips all the rest.

Rule 2: Only ONE branch ever runs

Even if multiple conditions would be True, only the first True branch executes. This is why order matters when writing elif chains.

Rule 3: else is the safety net

else has no condition — it runs when none of the above conditions were True. It's optional but gives your program a guaranteed fallback.

Watch how order changes the result:

❌ Wrong order — catches everything at 90+:

score = 95

if score >= 60:
    grade = "C"   # ← This runs first!
elif score >= 80:
    grade = "B"   # Never reached
elif score >= 90:
    grade = "A"   # Never reached

print(grade)  # "C"  — WRONG

✅ Correct order — highest first:

score = 95

if score >= 90:
    grade = "A"   # ← This runs!
elif score >= 80:
    grade = "B"
elif score >= 60:
    grade = "C"

print(grade)  # "A"  — CORRECT

When using >= ranges, always check the most specific / highest condition first.

1. The if Statement

The if statement is the simplest form of control flow. It checks a condition and runs code only if that condition is True.

code to run if True

PartDescription
ifKeyword that starts the condition check
conditionAn expression that evaluates to True or False
:Required colon at the end of the line
Indented codeCode that runs only if condition is True (4 spaces)
# Simple if statement
age = 18

if age >= 18:
    print("You can vote!")
    print("You're an adult!")

print("This always prints")

# Try changing age to 15 and see what happens!

# ✅ Expected output:
# You can vote!
# You're an adult!
# This always prints

2. The if-else Statement

What if you want to do something different when the condition is False? Use else to specify an alternative action.

If condition is True:

Run the first block, skip the else block

If condition is False:

Skip the first block, run the else block

# if-else example
temperature = 25

if temperature > 30:
    print("It's hot outside!")
    print("Stay hydrated!")
else:
    print("The weather is nice.")
    print("Enjoy your day!")

# Try: Change temperature to 35

# ✅ Expected output:
# The weather is nice.
# Enjoy your day!

3. The if-elif-else Chain

When you have more than two possibilities, use elif (short for "else if") to check multiple conditions.

code if none are True

# Grading system example
score = 85

if score >= 90:
    grade = "A"
    print("Excellent work!")
elif score >= 80:
    grade = "B"
    print("Good job!")
elif score >= 70:
    grade = "C"
    print("Not bad!")
elif score >= 60:
    grade = "D"
    print("You passed.")
else:
    grade = "F"
    print("Need improvement.")

print(f"Your grade: {grade}")

# Try different scores: 95, 75, 55

# ✅ Expected output:
# Good job!
# Your grade: B

🧑‍🏫 Worked example: a cinema ticket price

Here is the same idea doing real work. Every line is commented with what it produces and why, so you can trace the program the way Python does — top to bottom, stopping at the first condition that is True. Notice the last if is a separate statement, not an elif: that is deliberate, and the comments explain what it changes.

# WORKED EXAMPLE: a cinema ticket price calculator.
# Read it top to bottom - that is exactly the order Python reads it in.

age = 15               # the customer's age
is_student = False     # do they have a student card?
day = "Tuesday"        # this cinema runs a Tuesday discount

# Python tests the chain below in order and runs ONLY the first True branch.
if age < 5:
    price = 0.00
    category = "Infant"          # under-5s go free
elif age < 16:
    price = 7.50
    category = "Child"           # 15 < 16 is True, so this branch wins
elif age >= 65:
    price = 8.00
    category = "Senior"
elif is_student:                 # never reached here - an earlier branch already matched
    price = 9.00
    category = "Student"
else:
    price = 12.00
    category = "Adult"           # the fallback when nothing above matched

# A separate if - NOT an elif - so this discount stacks on top of the category.
if day == "Tuesday":
    price = price - 2.00         # 7.50 - 2.00 = 5.50

print(f"Category: {category}")
print(f"You pay: £{price:.2f}")  # :.2f forces exactly two decimal places

# ✅ Output:
# Category: Child
# You pay: £5.50

# Experiment: set age = 70 and the Senior branch wins instead.
# Then set age = 20 and is_student = True - now the Student branch runs, because
# none of the age branches above it matched. Order really does decide the answer.

# ✅ Expected output:
# Category: Child
# You pay: £5.50

4. Comparison Operators in Conditions

Conditions use comparison operators to compare values. These always return True or False.

OperatorMeaningExampleResult
==Equal to5 == 5True
!=Not equal to5 != 3True
>Greater than10 > 5True
<Less than3 < 7True
>=Greater than or equal5 >= 5True
<=Less than or equal4 <= 4True

5. Combining Conditions with Logical Operators

You can combine multiple conditions using logical operators: and, or, and not.

OperatorDescriptionExample
andBoth conditions must be Trueage >= 18 and has_id
orAt least one condition must be Trueage < 12 or age > 65
notReverses the conditionnot is_banned
# Combining conditions
age = 25
has_ticket = True
is_vip = False

# Using 'and' - both must be True
if age >= 18 and has_ticket:
    print("Welcome to the concert!")

# Using 'or' - at least one must be True
if age < 12 or age > 65:
    print("You get a discount!")
else:
    print("Regular price applies.")

# Using 'not' - reverses the condition
if not is_vip:
    print("Please use the regular entrance.")

# Try changing the values!

# ✅ Expected output:
# Welcome to the concert!
# Regular price applies.
# Please use the regular entrance.

🎯 Your turn: the library borrowing checker

Time to write some of it yourself. The program below is finished apart from three blanks: one keyword, one comparison operator and one logical operator — the three things this lesson has taught you. Replace each ___ and run it. The expected output is in a comment at the bottom, so you can check yourself without leaving the page.

# 🎯 YOUR TURN - finish the library borrowing checker
# Replace each ___ below, then press Run.

age = 14            # the borrower's age
has_card = True     # do they have a library card?
fines_owed = 0.00   # unpaid fines, in pounds
book = "adult"      # which section the book came from: "child" or "adult"

if not has_card:
    print("You need a library card first.")
___ fines_owed > 0:                     # 👉 replace ___ with the keyword that means "otherwise, if"
    print(f"Pay your £{fines_owed:.2f} fine before borrowing.")
else:
    # An adult-section book needs the borrower to be 16 or over.
    if book == "child" or age ___ 16:   # 👉 replace ___ with the operator meaning "is at least"
        print("Enjoy your book!")
    else:
        print("Sorry, that book is for ages 16 and over.")

# A tip shown only when BOTH of these are true.
if age < 16 ___ fines_owed == 0:        # 👉 replace ___ with the logical operator meaning "both"
    print("Tip: the children's section is upstairs.")

# ✅ Expected output once the blanks are filled in:
# Sorry, that book is for ages 16 and over.
# Tip: the children's section is upstairs.

# Then experiment: set age = 17 and run again. You should get one line only:
# "Enjoy your book!" - the under-16 tip no longer applies.

⚡ Truthy and Falsy Values: Conditions Without ==

You don't always need to write == True or != "". Python evaluates any value as either truthy or falsy in a condition — and knowing this makes your code cleaner and more Pythonic.

Falsy values — treated as False

Truthy values — treated as True

# Checking if a variable has a value
name = ""
if name:                       # Same as: if name != ""
    print(f"Hello, {name}!")
else:
    print("No name provided")  # ← This runs

# Checking if a number is non-zero
score = 0
if not score:                  # Same as: if score == 0
    print("You haven't scored yet!")

# Checking if a list has items (you'll learn lists soon)
items = []
if items:
    print("Cart has items")
else:
    print("Your cart is empty")  # ← This runs

# 'None' check — very common in real code
result = None
if result is None:
    print("No result yet — still processing")

6. Nested If Statements

You can put an if statement inside another if statement. This is called nesting and is useful for complex decisions.

# Nested if example
age = 25
has_id = True

if age >= 18:
    print("You're old enough.")
    if has_id:
        print("ID verified. Entry allowed!")
    else:
        print("Please show your ID.")
else:
    print("Sorry, you must be 18 or older.")

# The inner if only runs if the outer if is True

# ✅ Expected output:
# You're old enough.
# ID verified. Entry allowed!

🛡️ Guard Clauses: Writing Cleaner Conditions

Deeply nested if statements are hard to read. Professional Python developers use a technique called guard clauses — checking for bad conditions early and exiting, which keeps the main logic flat and readable.

❌ The "pyramid of doom" — deeply nested:

def process_order(user, cart, payment):
    if user:
        if user["verified"]:
            if cart:
                if len(cart) > 0:
                    if payment:
                        print("Order placed!")
                    else:
                        print("No payment")
                else:
                    print("Cart empty")
            else:
                print("No cart")
        else:
            print("Not verified")
    else:
        print("No user")

5 levels deep — hard to follow, easy to make mistakes

✅ Guard clauses — flat and readable:

def process_order(user, cart, payment):
    if not user:
        print("No user")
        return

    if not user["verified"]:
        print("Not verified")
        return

    if not cart or len(cart) == 0:
        print("Cart empty")
        return

    if not payment:
        print("No payment")
        return

    print("Order placed!")  # Clean!

Each failure exits early — main logic is at ground level

The rule: reject bad cases first

Check for invalid input, missing data, or error conditions at the top. Once you've handled those, write the main happy-path logic without nesting. This is standard practice in professional codebases — it's called the early return pattern.

You'll use return properly when you learn functions in a later lesson. For now, just understand that flat code beats nested code.

7. Indentation: Python's Secret Sauce

Indentation is not optional in Python! It tells Python which code belongs to which block. Use 4 spaces (or 1 tab) for each level.

if age >= 18:
    print("Adult")
    print("Can vote")
if age >= 18:
print("Adult")  # Error!

Common Mistakes to Avoid

if x = 5: is wrong. Use if x == 5:

if x == 5 is wrong. Add colon: if x == 5:

Wrong capitalization of True/False

true and TRUE don't work. Use True and False

Putting elif after else

else must always come last in a chain

8. The Ternary Operator (One-Line If)

Python has a shorthand way to write simple if-else statements in one line. This is called the ternary operator or conditional expression.

value_if_true if condition else value_if_false

# Normal if-else
age = 20
if age >= 18:
    status = "Adult"
else:
    status = "Minor"
print(status)

# Same thing with ternary operator
age = 20
status = "Adult" if age >= 18 else "Minor"
print(status)

# Practical example
score = 75
result = "Pass" if score >= 60 else "Fail"
print(f"Result: {result}")

# ✅ Expected output:
# Adult
# Adult
# Result: Pass

🔀 match / case — Python's Modern Switch Statement (Python 3.10+)

Python 3.10 introduced match/case — a cleaner way to check a value against multiple exact options. Think of it as a smarter, more readable alternative to a long elif chain when you're matching against specific values.

Old way — elif chain:

command = "quit"

if command == "start":
    print("Starting...")
elif command == "stop":
    print("Stopping...")
elif command == "pause":
    print("Pausing...")
elif command == "quit":
    print("Quitting...")
else:
    print("Unknown command")
command = "quit"

match command:
    case "start":
        print("Starting...")
    case "stop":
        print("Stopping...")
    case "pause":
        print("Pausing...")
    case "quit":
        print("Quitting...")
    case _:
        print("Unknown command")

The case _: at the bottom is the wildcard — it matches anything that didn't match above (equivalent to else).

match/case with multiple values per case:

day = "Saturday"

match day:
    case "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday":
        print("It's a weekday — get to work!")
    case "Saturday" | "Sunday":
        print("It's the weekend!")
    case _:
        print("That's not a day of the week")

The | symbol means "or" inside a case — very handy for grouping related values.

9. Practical Examples

Example 1: Login Validator

# Simple login system
correct_username = "admin"
correct_password = "secret123"

# Simulated user input
entered_username = "admin"
entered_password = "secret123"

if entered_username == correct_username and entered_password == correct_password:
    print("Login successful! Welcome!")
else:
    print("Invalid username or password.")

# Try changing the entered values

# ✅ Expected output:
# Login successful! Welcome!

Example 2: Number Classifier

# Check if a number is positive, negative, or zero
number = -5

if number > 0:
    print(f"{number} is positive")
elif number < 0:
    print(f"{number} is negative")
else:
    print("The number is zero")

# Bonus: Check if even or odd
if number != 0:
    if number % 2 == 0:
        print("And it's even!")
    else:
        print("And it's odd!")

# ✅ Expected output:
# -5 is negative
# And it's odd!

Example 3: Age Classifier

# Classify age into categories
age = 25

if age < 0:
    print("Invalid age!")
elif age < 13:
    print("Child")
elif age < 20:
    print("Teenager")
elif age < 60:
    print("Adult")
else:
    print("Senior")

# Try different ages: 5, 15, 35, 70

# ✅ Expected output:
# Adult

🌍 Real-World Control Flow Patterns

The same handful of patterns appear in almost every real program. Recognise them and you'll be able to write production-quality logic from day one.

Pattern 1: Input validation — reject bad data first

username = "Al"
password = "abc"

if len(username) < 3:
    print("Username must be at least 3 characters")
elif len(password) < 8:
    print("Password must be at least 8 characters")
elif " " in username:
    print("Username cannot contain spaces")
else:
    print("Account created successfully!")

Validate before processing. Always check for invalid states first and give the user clear, specific feedback.

Pattern 2: Range banding — categorise a value

bmi = 22.5

if bmi < 18.5:
    category = "Underweight"
    advice   = "Consider eating more nutritious food"
elif bmi < 25.0:
    category = "Healthy weight"
    advice   = "Keep up your current lifestyle!"
elif bmi < 30.0:
    category = "Overweight"
    advice   = "Consider more physical activity"
else:
    category = "Obese"
    advice   = "Please consult a healthcare professional"

print(f"BMI: {bmi} — {category}")
print(f"Advice: {advice}")

Range banding is used everywhere: credit scores, age groups, tax brackets, shipping rates, difficulty levels.

Pattern 3: Default value — use a fallback if nothing set

# User may or may not have set a display name
display_name = ""
username     = "alice_99"

# Use display_name if it exists, otherwise fall back to username
name_to_show = display_name if display_name else username
print(f"Welcome, {name_to_show}!")  # "Welcome, alice_99!"

# Common pattern: default settings
user_language = None
language = user_language if user_language else "English"
print(f"Language: {language}")  # "Language: English"

Pattern 4: Feature flags — enable/disable behaviour

# Control features with simple booleans
DEBUG_MODE    = True
SHOW_ADS      = False
PREMIUM_USER  = True

if DEBUG_MODE:
    print("[DEBUG] Application started")

if PREMIUM_USER:
    print("Enjoy your ad-free experience!")
elif SHOW_ADS:
    print("[Ad: Learn Python Fast!]")

message = "Premium member" if PREMIUM_USER else "Free tier"
print(f"Account: {message}")

Feature flags let you turn functionality on/off with a single variable change — widely used in production software for A/B testing and gradual rollouts.

Mini-Project: Smart Shopping Discount Calculator

This project uses every control flow concept from this lesson — if/elif/else, logical operators, truthy/falsy, ternary, and nested conditions — to solve a real business problem.

# Smart Shopping Discount Calculator
item_price   = 85.00
quantity     = 3
is_member    = True
promo_code   = "SAVE10"
VALID_CODES  = ["SAVE10", "SUMMER20", "WELCOME5"]

# === Validate inputs ===
if item_price <= 0:
    print("Error: price must be greater than zero")
elif quantity <= 0:
    print("Error: quantity must be at least 1")
else:
    subtotal = item_price * quantity

    # === Apply bulk discount ===
    if quantity >= 10:
        bulk_discount = 0.15    # 15% for 10+
    elif quantity >= 5:
        bulk_discount = 0.10    # 10% for 5+
    elif quantity >= 3:
        bulk_discount = 0.05    # 5% for 3+
    else:
        bulk_discount = 0.00    # No bulk discount

    # === Member discount ===
    member_discount = 0.08 if is_member else 0.00

    # === Promo code discount ===
    if promo_code and promo_code in VALID_CODES:
        if promo_code == "SUMMER20":
            promo_discount = 0.20
        elif promo_code == "SAVE10":
            promo_discount = 0.10
        else:
            promo_discount = 0.05
    else:
        promo_discount = 0.00
        if promo_code:
            print(f"Note: '{promo_code}' is not a valid promo code")

    # === Calculate total (discounts don't stack beyond 25%) ===
    total_discount = min(bulk_discount + member_discount + promo_discount, 0.25)
    discount_amount = subtotal * total_discount
    final_price = subtotal - discount_amount

    # === Display receipt ===
    membership_label = "Member" if is_member else "Guest"
    print("=" * 35)
    print(f"  RECEIPT ({membership_label})")
    print("=" * 35)
    print(f"Item price:     £{item_price:.2f}")
    print(f"Quantity:       {quantity}")
    print(f"Subtotal:       £{subtotal:.2f}")
    print(f"Bulk discount:  {bulk_discount*100:.0f}%")
    print(f"Member discount:{member_discount*100:.0f}%")
    print(f"Promo discount: {promo_discount*100:.0f}%")
    print(f"Total saved:    £{discount_amount:.2f} ({total_discount*100:.0f}%)")
    print(f"TOTAL:          £{final_price:.2f}")
    print("=" * 35)

Can you add a check that prints "Free delivery!" if final_price >= 50? What about adding a loyalty points calculation: 1 point per £1 spent, but double points for members?

💡 Thinking in Conditions: How Developers Design Logic

Writing correct if/elif/else logic is a skill. Here's the thinking process professionals use when turning requirements into code.

Step 1: List all possible outcomes

Before writing a single line, ask: "What are all the different things that could happen?" Write them in plain English first.

# Requirement: categorise a user's subscription
# Outcomes:
# - No subscription → show free tier
# - Basic plan → show basic features
# - Pro plan → show pro features + no ads
# - Admin → show all features + admin panel

Step 2: Identify edge cases

What happens with invalid, unexpected, or extreme values? Always ask:

Step 3: Decide the order

For range checks: put the most specific condition first. For validation: check the most likely failure first. For priority: most important condition at the top.

Step 4: Test with examples

Manually trace through your code with at least 3 test values — one for each branch, plus one edge case. Ask: "What value do I need to reach the else?" If you can't answer, your logic might have a gap.

Summary: Quick Reference

ConceptWhat It DoesSyntax
ifRuns code if condition is Trueif x > 0:
elseRuns if the if condition is Falseelse:
elifChecks another conditionelif x > 5:
andBoth conditions must be Trueif a and b:
orAt least one must be Trueif a or b:
notReverses the conditionif not a:
TernaryOne-line if-elsex if cond else y

🎯 Mini Challenge: Parcel Postage Calculator

No blanks this time — just a brief and an empty space. Work out the postage for a parcel using the rules below. Everything you need you learned in this lesson: an if/elif/else chain for the weight bands, and separate if statements for the surcharges that stack on top.

Stuck? Look back at the cinema ticket worked example — it has exactly this shape: one chain that picks a band, then separate ifs that adjust the running total.

# 🎯 MINI-CHALLENGE: parcel postage calculator

weight_kg = 6.5
distance_km = 250
is_express = True

# 1. Create a variable "total" starting at 3.99 (the base cost)
#
# 2. Weight surcharge - ONE if/elif/else chain:
#      over 10 kg -> add 5.00
#      over 2 kg  -> add 2.00
#      otherwise  -> add nothing
#
# 3. A separate if: when distance_km is more than 100, add 4.50
#
# 4. A separate if: when is_express is True, multiply total by 2
#
# 5. Print exactly two lines:
#      f"Parcel: {weight_kg} kg, {distance_km} km"
#      f"Total: £{total:.2f}"

# your code here

# ✅ Expected output:
# Parcel: 6.5 kg, 250 km
# Total: £20.98
#
# Watch the order in step 2: if you test "over 2 kg" before "over 10 kg",
# a 12 kg parcel matches the wrong band and you undercharge by 3.00.

What's Next?

Now that you can make decisions, it's time to learn about loops! Loops let you repeat code multiple times, which is essential for:

Lesson complete — your programs can now make decisions!

You've mastered if, elif, else, logical operators, nested conditions, and the ternary expression. Your programs are no longer just top-to-bottom scripts — they can respond to different situations.

Practice quiz

In an if/elif/elif chain, how many branches run when several conditions are True?

  • All matching branches
  • Only the last True branch
  • Only the first True branch
  • None of them

Answer: Only the first True branch. Python checks top to bottom and runs only the first True branch, skipping the rest.

When using >= range checks like grades, which condition should come first?

  • The highest / most specific threshold
  • The lowest threshold
  • The else branch
  • Order does not matter

Answer: The highest / most specific threshold. With >= ranges, check the highest threshold first, or a broad early condition catches everything.

What does the else clause do in an if/elif/else chain?

  • Checks one more condition
  • Always runs
  • Runs before the if
  • Runs only when none of the above conditions were True

Answer: Runs only when none of the above conditions were True. else has no condition; it runs as a fallback when no earlier condition was True.

Which of these is a FALSY value in Python?

  • "0" (the string)
  • [] (empty list)
  • [0] (list with one item)
  • -5

Answer: [] (empty list). An empty list is falsy. A non-empty string "0", a non-empty list [0], and -5 are all truthy.

What does the ternary expression "Adult" if age >= 18 else "Minor" return when age is 20?

  • "Adult"
  • "Minor"
  • True
  • 20

Answer: "Adult". Since 20 >= 18 is True, the expression returns "Adult".

What is the difference between = and == in a condition?

  • They are interchangeable
  • == assigns; = compares
  • = assigns a value; == compares for equality
  • Both compare values

Answer: = assigns a value; == compares for equality. = assigns a value to a variable; == tests whether two values are equal.

Which is the recommended (PEP 8) way to check for None?

  • if value == None:
  • if value is None:
  • if value = None:
  • if not value:

Answer: if value is None:. PEP 8 recommends 'is None' — it's more readable and technically correct.

What does the | symbol mean inside a match/case pattern, e.g. case "Saturday" | "Sunday":?

  • Bitwise OR of bytes
  • A pipe to another function
  • String concatenation
  • 'or' — the case matches any of the listed values

Answer: 'or' — the case matches any of the listed values. Inside a case, | means 'or', so the case matches any of the listed values.

What does case _: do in a match statement?

  • Matches only underscores
  • Acts as the wildcard / default, like else
  • Raises an error
  • Matches nothing

Answer: Acts as the wildcard / default, like else. case _: is the wildcard that matches anything not matched above, equivalent to else.

What is the key idea behind a guard clause (early return pattern)?

  • Nest every condition deeply
  • Always use match/case
  • Reject bad cases early and return, keeping the main logic flat
  • Avoid using else

Answer: Reject bad cases early and return, keeping the main logic flat. Guard clauses check for invalid cases first and exit early, keeping the happy path flat and readable.

Continue this course