← Home

066. Return Values

Functions communicate results through return

066. Return Values

Aryan’s monitor is a pipeline: raw readings feed into classifiers, which feed into formatters, which feed into the display. Each function must return its result for the next function to consume:

def get_memory_pct():
    mem = psutil.virtual_memory()
    return mem.percent         # return, not print

def classify(pct):
    if pct >= 90: return "CRITICAL"
    if pct >= 75: return "WARNING"
    return "OK"

def format_status(pct, level):
    bar = "#" * int(pct / 5)
    return f"[{bar:<20}] {pct:.1f}%  {level}"

# Pipeline: each function feeds the next
pct    = get_memory_pct()
level  = classify(pct)
status = format_status(pct, level)
print(status)

If classify() used print() instead of return, format_status would receive None and crash. print() is for the end of the pipeline — the display layer. Everything before it must return.

Rule: if a function’s result will be used by another function, it must return. Using print() instead of return turns a reusable function into a dead end that can’t be tested or composed.


💡 Fun fact: In early BASIC, there was no return statement for values — subroutines communicated through shared global variables. The concept of a function returning a value to its caller, enabling pipelines like Aryan’s, was popularized by languages like Algol and later C in the 1970s.

⚠️ Watch out: The classic beginner mistake is calling print() inside a function thinking the caller will receive the printed value. print() always returns None — the value goes to the screen, not to the caller. This causes TypeError crashes that are confusing until you understand the distinction.

🤔 Think about it: If a function calls print() and also has a return statement, what does the caller receive? Can you think of a case where you’d intentionally want a function to both print and return a value?

Learning objectives

  • Use return to send values back from functions
  • Understand the difference between return and print
  • Return multiple values as a tuple

Key concepts

  • return
  • return value
  • print vs return

Try it

Concept detail

return exits the function and sends a value back to the caller. Without return, functions return None — a silent trap when the caller uses the result.

print vs return: print(x) — displays x to the console; function still returns None return x — sends x to the caller; execution ends here

A function that only prints is a dead end: result = my_func() # result is None if my_func only prints other_func(result) # crash: other_func gets None

Multiple return values (implicit tuple): def min_max(nums): return min(nums), max(nums) # returns a tuple

lo, hi = min_max([3, 1, 4]) # unpacked at call site

Early return: def find(items, target): for item in items: if item == target: return item # exits immediately return None # explicit fallback

A function that returns None by default should make that explicit: return None.

Solution

def mean(numbers):
    total = sum(numbers)
    avg = total / len(numbers)
    return avg

def variance(numbers):
    avg = mean(numbers)
    squared_devs = [(x - avg) ** 2 for x in numbers]
    result = sum(squared_devs) / len(numbers)
    return result

def min_max(numbers):
    return min(numbers), max(numbers)

Tests

def test_mean():
    assert mean([1, 2, 3, 4, 5]) == 3.0

def test_mean_not_none():
    result = mean([1, 2, 3])
    assert result is not None

def test_mean_single():
    assert mean([42]) == 42.0

def test_variance():
    # Classic dataset: [2,4,4,4,5,5,7,9], variance = 4.0
    assert abs(variance([2, 4, 4, 4, 5, 5, 7, 9]) - 4.0) < 0.001

def test_variance_equal():
    # All equal → variance is 0
    assert variance([1, 1, 1]) == 0.0

def test_variance_not_none():
    assert variance([1, 2, 3]) is not None

def test_min_max():
    mn, mx = min_max([3, 1, 4, 1, 5, 9])
    assert mn == 1
    assert mx == 9

Resources