NOETRION

Choose a topic

LESSON 6 OF 9 · Python & data foundations
PYTHON BASICS · 4 MIN READ

Functions and small tests: make a calculation reusable

Separate inputs, return values, and display, then verify a function with cases you can calculate by hand.

Learning lesson · Reviewed October 2, 2026. Allow additional time for the practice. Prerequisite: Conditions and loops. Python 3; no extra packages.

What you will learn
  • Write a function with an explicit input and output.
  • Distinguish returning a value from printing it.
  • Use normal, boundary, and empty cases to check a calculation.

Repeating a calculation in several places creates several opportunities for it to drift. A function gives the calculation one name and one place to inspect. In this lesson, the task is to calculate the arithmetic mean of a list. The fictional observations are small enough that you can check every expected result by hand.

Write the contract before the function

Our contract is: accept a list of numbers, return its mean, and return None when the list is empty. Input validation is a separate task introduced later. The contract does not say that the function prints a report, rounds the answer, or edits the list. Keeping those decisions outside the calculation makes it easier to reuse.

def average(values):
    """Return the arithmetic mean, or None for no observations."""
    if len(values) == 0:
        return None
    return sum(values) / len(values)

result = average([10, 20, 30])
print(result)  # 20.0

The name values is a parameter: inside a call, it refers to the supplied list. A return sends a result to the caller and ends that function call. The caller can display the result, compare it, or pass it to another function. print() displays text; displaying the correct number alone does not give the caller that number as a return value.

Caller
[10, 20, 30]
Calculation
60 / 3
Return
20.0
Display
print(result)
The calculation returns a number. The caller chooses how to use or display it.

Test with answers that exist independently of the code

A useful expected answer comes from the contract and a hand calculation, not from copying what the function just printed. For [2, 4], the total is 6 and the count is 2, so the expected answer is 3. For one observation, the mean is that observation. For no observations, our chosen contract returns no numeric answer.

assert average([2, 4]) == 3.0
assert average([7]) == 7.0
assert average([]) is None
assert average([-2, 2]) == 0.0
print("4 checks passed")

Each assert expresses a check; a false condition raises AssertionError in an ordinary Python run. These tiny checks help learning and debugging. Do not use assertions to validate untrusted input in a deployed application: Python can disable them in optimized execution. For production projects, use a test runner and explicit validation.

CaseExpected resultWhat it can expose
Two observations3.0 for [2, 4]Wrong total or denominator
One observation7.0 for [7]Off-by-one denominator
Empty inputNoneDivision by zero or invented zero
Negative and positive0.0 for [-2, 2]Incorrect handling of signs

Keep precision and presentation separate

A displayed value such as 2.33 can be convenient while the underlying result contains more precision. Round at the presentation boundary unless your task explicitly requires intermediate rounding. Binary floating-point arithmetic can produce small representation differences, so exact equality is not appropriate for every numeric test. For a calculated non-integer result, a tolerance check can express the intended comparison.

from math import isclose
assert isclose(average([1, 2, 4]), 7 / 3)
print(f"Mean: {average([1, 2, 4]):.2f}")  # Mean: 2.33

Practice: count observations meeting a threshold

Write count_at_least(values, threshold). It should return a count, leave the list unchanged, and return zero for an empty list. Check [59, 60, 80] with a threshold of 60. Decide the expected answer before writing the implementation.

Show the worked solution
def count_at_least(values, threshold):
    count = 0
    for value in values:
        if value >= threshold:
            count += 1
    return count

assert count_at_least([59, 60, 80], 60) == 2
assert count_at_least([], 60) == 0
assert count_at_least([60], 60) == 1

This empty count is zero because it answers how many observations matched. An empty mean is different: there is no observed number to average.

Before moving on

Explain the contract in one sentence, identify what the function returns, and show a test that would fail if >= accidentally became >. The next lesson uses dictionaries to attach names to the fields that these functions process.

Sources and further reading

Official sources checked October 2, 2026. Examples, exercises, and rubrics are original Noetrion teaching material; fictional datasets are identified below.

Check your understanding

A function calculates and prints 20 but has no return statement. What value does the caller receive?

Choose one answer