Skip to main content

Branches: if/elif/else

James opens a SmartNotes function and finds a line he has not written before: if word_count > 1000:. He stares at the colon at the end, then at the indented block below it.

"You read one of these in Chapter 49," Emma says, pulling up the AI-generated code they reviewed together. "You predicted what it did. Now you write your own."

James nods slowly. He remembers seeing if filename.endswith('.py') back in Chapter 43's axiom examples too. The pattern looked straightforward when he was reading it. Writing it himself feels different. Emma points at the screen. "Think of it like a flowchart. If this condition is true, do this. Otherwise, do that. The colon and indentation tell Python where each path starts and stops."

If you're new to programming

A branch lets your program choose between different actions based on a condition. Instead of running every line top to bottom, the program checks a condition (like "is word_count greater than 1000?") and takes one path or another. Almost every useful program contains branches. They are how software makes decisions.

If you know branches from another language

Python uses if, elif (not else if), and else. There are no curly braces; indentation defines the blocks. The condition does not need parentheses (though they are allowed). Python also supports chained comparisons like 18 <= age < 65, which most languages do not.


Your First if Statement

The simplest branch checks one condition. If the condition is True, the indented block runs. If the condition is False, Python skips the block entirely:

word_count: int = 350

if word_count > 1000:
print("This note is long.")

Output:

(no output)

Because 350 is not greater than 1000, the indented line never runs. Python moves on to whatever comes after the if block.


Adding else

An else block catches everything the if condition missed:

word_count: int = 350

if word_count > 1000:
print("This note is long.")
else:
print("This note is not long.")

Output:

This note is not long.

Exactly one of the two blocks runs. Never both. Never neither.


Chaining with elif

When you need more than two paths, add elif (short for "else if") between if and else. Python checks each condition top to bottom and runs the first block whose condition is True:

word_count: int = 350

if word_count > 1000:
category: str = "long"
elif word_count > 200:
category = "medium"
else:
category = "short"

print(category)

Output:

medium

Order matters. Python stops at the first match. Because 350 > 200 is True, the elif block runs and Python skips else entirely. Emma's rule: put the most specific conditions first. If you checked word_count > 200 before word_count > 1000, every long note would be classified as "medium" because 1500 > 200 is also True.


Block Indentation

Block indentation works the same way as function bodies: 4 spaces in. Every line inside an if, elif, or else block must be indented by exactly 4 spaces relative to the if keyword:

word_count: int = 1200

if word_count > 1000:
category: str = "long"
print("Flagging for review.") # Same block, same indent

print("Done.") # Outside the block, always runs

Output:

Flagging for review.
Done.

The print("Done.") line is not indented under the if, so it runs regardless of the condition. Mixing up indentation is the most common beginner mistake with branches.


Chained Comparisons

Sometimes you need to check whether a value falls within a range. You already know how to do this with and:

age: int = 25
is_working_age: bool = age >= 18 and age < 65 # known pattern
print(is_working_age)

Output:

True

Python offers a shorthand that reads more naturally:

age: int = 25
is_working_age_v2: bool = 18 <= age < 65 # Python shorthand
print(is_working_age_v2)

Output:

True

Both lines do the same thing. The chained version 18 <= age < 65 mirrors how you would say it in English: "age is between 18 and 65." Use whichever feels clearer to you; the and version is always correct when you are unsure.


Truthiness in Conditions

You learned in Chapter 47, Lesson 3 that bool() converts values to True or False. Python uses the same conversion when evaluating if conditions. Empty strings, zero, and None are all "falsy":

title: str = ""

if title:
print("Note has a title.")
else:
print("Title is missing.")

Output:

Title is missing.

Because bool("") == False, the if block is skipped. A non-empty string like "My Note" would be truthy, and the first block would run instead.

SmartNotes Connection

In SmartNotes, you might check whether a user provided a title before saving. Writing if title: is shorter than if title != "":and both do the same thing. You will see both styles in real code.


Functions with Branches

Branches become powerful inside functions. Here is a function that categorizes a note by word count and returns the category as a string:

def categorize_note(word_count: int) -> str:
"""Categorize a note by its word count."""
if word_count > 1000:
return "long"
elif word_count > 200:
return "medium"
else:
return "short"

Each return exits the function immediately. Only one branch runs, so only one value is returned.


Testing Which Branch Runs

How do you know every branch works correctly? You write a test for each one:

def test_categorize_long() -> None:
assert categorize_note(1500) == "long"

def test_categorize_medium() -> None:
assert categorize_note(500) == "medium"

def test_categorize_short() -> None:
assert categorize_note(100) == "short"

Three branches, three tests. Each test picks an input that should trigger exactly one branch, then asserts the expected result. If any assert fails, you know which branch is broken. Run these with uv run pytest and all three should pass.


PRIMM-AI+ Practice: Branch Predictions

Predict [AI-FREE]

Look at this code without running it. For each print call, predict whether it will execute. Write your predictions and a confidence score from 1 to 5 before checking.

score: int = 72

# Prediction 1
if score >= 90:
print("A")

# Prediction 2
elif score >= 80:
print("B")

# Prediction 3
elif score >= 70:
print("C")

# Prediction 4
else:
print("F")
Check your predictions

Prediction 1: print("A") does NOT run. 72 >= 90 is False.

Prediction 2: print("B") does NOT run. 72 >= 80 is False.

Prediction 3: print("C") RUNS. 72 >= 70 is True, and this is the first true condition.

Prediction 4: print("F") does NOT run. Python already found a match at the elif score >= 70 branch.

Only C prints. If you got all four correct, your branch-tracing intuition is solid.

Run

Create a file called branch_practice.py with the code above. Run uv run python branch_practice.py. Compare the output to your predictions.

Investigate

Change score to 95, then to 50, then to 80. Before each run, predict which letter prints. After running, trace through the conditions top to bottom and explain why that branch matched.

Modify

Add a new branch for "D" grades (scores between 60 and 69). Where does it go in the chain? Predict the output for score = 65, then run and verify.

Hint for where to place the new branch

The new elif score >= 60 must go after the elif score >= 70 check but before else. Remember: Python checks conditions top to bottom and stops at the first match.

Make [Mastery Gate]

Without looking at any examples, write a function called categorize_by_length(word_count: int) -> str that returns:

  • "essay" for word counts above 2000
  • "article" for word counts above 500
  • "note" for word counts above 100
  • "snippet" for everything else

Then write four test functions (one per branch). Run uv run pytest to verify all tests pass.


Exercises

Exercise 1: Spot the Bug

This AI-generated function has a bug in the elif ordering. Find it and fix it:

def classify_temperature(temp: float) -> str:
"""Classify a temperature reading."""
if temp > 0:
return "above freezing"
elif temp > 30:
return "hot"
elif temp > 20:
return "warm"
else:
return "freezing"
Hint

Try calling classify_temperature(35). What does it return? What should it return? Think about which condition Python checks first.

Solution

The bug: temp > 0 is checked first, so any positive temperature (including 35 or 25) returns "above freezing". The fix is to reorder from most specific to least specific:

def classify_temperature(temp: float) -> str:
"""Classify a temperature reading."""
if temp > 30:
return "hot"
elif temp > 20:
return "warm"
elif temp > 0:
return "above freezing"
else:
return "freezing"

Exercise 2: Write the Tests

Write a function grade_score(score: int) -> str that returns letter grades:

  • 90 and above: "A"
  • 80 to 89: "B"
  • 70 to 79: "C"
  • 60 to 69: "D"
  • Below 60: "F"

Then write five test functions, one for each branch. Each test should pick a score that triggers exactly one branch.

Starter code
def grade_score(score: int) -> str:
"""Return a letter grade for the given score."""
# Your if/elif/else chain here
...

def test_grade_a() -> None:
assert grade_score(95) == "A"

# Write test_grade_b, test_grade_c, test_grade_d, test_grade_f

Exercise 3: Parsons Problem

These lines form a working function, but they are scrambled. Rearrange them into the correct order:

    return "urgent"
def prioritize_note(days_old: int) -> str:
return "archive"
elif days_old > 7:
return "review"
else:
if days_old > 30:
"""Prioritize a note based on how old it is."""
Hint 1

A function definition always comes first, followed by its docstring on the next line.

Hint 2

The most specific condition (largest threshold) should be checked first. What is the largest number in the conditions?

Solution
def prioritize_note(days_old: int) -> str:
"""Prioritize a note based on how old it is."""
if days_old > 30:
return "archive"
elif days_old > 7:
return "review"
else:
return "urgent"

Try With AI

Opening Claude Code

If Claude Code is not already running, open your terminal, navigate to your SmartNotes project folder, and type claude. If you need a refresher, Chapter 44 covers the setup.

Prompt 1: Check Your Understanding

I just learned about if/elif/else in Python. Here is my
understanding: "Python checks every condition in an
if/elif/else chain and runs all the blocks that match."
Is my summary accurate? What am I getting wrong?

Read the AI's response carefully. Did it correct the "runs all blocks" claim? Python runs only the first matching block, then skips the rest. Compare its explanation to what you learned in this lesson.

Prompt 2: Generate and Review a Branching Function

Write a Python function called rate_password_strength that
takes a password string and returns "strong", "medium", or
"weak" based on its length. Use type annotations on all
variables and the return type. Include a docstring.

Review the AI's output. Check: are the elif conditions ordered from most specific to least specific? Are all variables type-annotated? Does the function have a return type? If anything looks wrong, tell the AI what to fix.

What you're learning: You are applying the ordering rule from this lesson to evaluate AI-generated code.


Key Takeaways

  1. if/elif/else lets your program choose between paths. Python checks conditions top to bottom and runs the first block whose condition is True.

  2. Order matters. Put the most specific conditions first. If temp > 0 comes before temp > 30, hot temperatures never reach the "hot" branch.

  3. Block indentation is the same rule you already know. if, elif, and else blocks use 4 spaces of indentation, just like function bodies.

  4. Chained comparisons simplify range checks. 18 <= age < 65 does the same thing as age >= 18 and age < 65, but reads more naturally.

  5. Test every branch. Write one test per branch. If a function has three return paths, write three tests. Each test should trigger exactly one branch.


Looking Ahead

You can now make your programs choose between paths. In Lesson 2, you will learn for loops, which let your programs repeat actions across collections of data. Instead of categorizing one note, you will categorize all of them.