return is not print
Mixing up return and print is the most common design bug in beginner functions, and the difference has teeth: a returned value can be stored, compared, tested, and fed into the next function, while a printed one is gone the moment it hits the screen. Getting return exactly right is what makes functions combine into bigger programs.
print shows a value to a human and gives nothing back. return hands a value to the calling code, silently. A function that computes should return, so callers can keep working with the result: store it, compare it, pass it onward.
Two more rules with teeth:
returnends the function instantly. Any code after the executed return never runs.- No return means
None. Fall off the end of a function and the caller receivesNone.
Rule 1 enables the tidy early return style, no else needed:
def classify(n): if n < 0: return "negative" if n == 0: return "zero" return "positive"
Reaching the last line already proves both ifs failed, the same top-to-bottom logic as your elif chains in lesson 4-1.
Three calls, three exits
Each call below leaves the function at a different return, and none of the later lines run once one has fired.
def classify(n): if n < 0: return "negative" if n == 0: return "zero" return "positive" print(classify(-5)) print(classify(0)) print(classify(3))
Output
negative zero positive
The call with -5 satisfied the first condition and left immediately, so the n == 0 test was never evaluated. The call with 0 failed the first test and passed the second. The call with 3 failed both, and reaching the final line is itself the proof that the number is positive.
That is what makes the early-return style work without any else. Each return acts as a guard, and every line below it can assume the conditions above it were false.
This snippet prints HI and then None.
def shout(word): print(word.upper()) result = shout("hi") print(result)
The call does display HI, because the function prints it. But shout contains no return, so it hands back None, and that None is what lands in result and appears on the second line.
The fix depends on the intent. Having result hold "HI" requires return word.upper() instead of printing it, leaving the caller to decide whether to display the value. That separation is the general rule for functions that compute something: return the result, and let the calling code choose what to do with it.
Defining is_even
A predicate is a function that answers a yes-or-no question, and its body is often a single comparison.
def is_even(n): return n % 2 == 0 print(is_even(4)) print(is_even(7))
Output
True False
The test is the one from lesson 1-3: a number is even when dividing by 2 leaves no remainder. What is worth noticing is that n % 2 == 0 already evaluates to a bool, so it can be returned directly. Wrapping it in if n % 2 == 0: return True with an else: return False would produce identical results with four times the code.
Returning the comparison rather than printing it is also what makes the function composable, since if is_even(x): reads naturally at the call site.
This snippet prints 14.
def f(x): return x * 2 print(f(3) + f(4))
Each call is evaluated separately, so f(3) produces 6 and f(4) produces 8, and the + then adds those two numbers before print ever runs.
This composition is the entire reason return exists. Because the calls hand back plain numbers, they can sit inside a larger expression, be stored, or be passed to another function. Had f printed its result instead, the same line would display 6 and 8 on their own lines and then fail while trying to add None to None.