Designing Branching Functions
Introduction: Decisions Inside a Function
Welcome to Writing Complex Python Functions, the fourth course in this path and its opening unit. Up to now, our functions have followed a single, straight path: take some inputs, compute one result, and return it. Real functions rarely behave that way. They usually need to choose what to return based on the data they receive.
Our running example for this lesson is classify_score(score), a function that turns a numeric score into a letter grade. By the end, running our program will print exactly this:
Four calls, four different answers, and one function. Three ideas get us there: one return per branch, correct branch ordering, and guard clauses that reject bad input before anything else runs.
One `return` Per Branch
Let's start with the grading logic itself, without any input checking yet. Inside a function body, an if / elif chain lets each branch end in its own return statement.
As we may recall from the previous course, return immediately ends the function; nothing after it in that call ever runs. That behavior is what makes this pattern work. Let's trace classify_score(95):
- Python evaluates
95 >= 90, which isTrue. - It runs
return "A", and the function ends right there. - The
elif score >= 80andelif score >= 70conditions are never evaluated at all.
So each branch is a separate, self-contained answer. Only one of them can ever produce the result for a given call.
The Fallback `return` at the End of the Body
The version above has a hole in it. What does classify_score(41) return? No condition matches, so execution reaches the end of the body without ever hitting a return, and the call quietly produces None: a bug we learned to spot in the previous course. Let's close that hole.
Notice that return "F" sits at the same indentation level as the if, not attached to an else. It is plain code at the end of the function body, so it runs only when every branch above fails to match and therefore does not return. Reading it this way is natural: it is the "everything else" answer. Writing else: return "F" would behave identically here, so the choice is purely about readability. Either way, every path through the function now ends in a return.

