Skip to content
aviral gupta

// B3.1 · ~25 min · Beginner

Defining functions and returning values

After this lesson you can define a function with def, call it with arguments, hand a result back with return, and explain why a function without return gives None.

Lesson 1 of 5 in B3 Functions

Start of the module

You will be able to

  • Define a function with def and parameters, and call it with arguments
  • Return a value with return, and predict when a function returns None
  • Keep variables local to a function, and give it a one-line docstring
  1. Warm-up · Activity 1 of 7

    Warm-up from module B2: what does this print?

    total = 0
    for n in range(1, 4):
        total += n
    print(total)
  2. Predict · Activity 2 of 7

    Predict before you read on: what does this print?

    def double(n):
        n * 2
    
    print(double(5))
  3. Practice · Activity 3 of 7

    Fill in the keyword so that area(3, 4) gives the caller 12.

    def area(width, height):
        ____ width * height
    width * height
  4. Practice · Activity 4 of 7

    A variable inside a function has the same name as one outside. What does this print?

    def set_score():
        score = 100
    
    score = 5
    set_score()
    print(score)
  5. Practice · Activity 5 of 7

    Given def f(x): return x + 1 and def g(x): print(x), match each expression to what it gives back.

  6. Brain teaser · Activity 6 of 7

    Brain teaser. return sits inside a for loop. What does this print?

    def first_even(numbers):
        for n in numbers:
            if n % 2 == 0:
                return n
        return -1
    
    print(first_even([3, 7, 8, 10]), first_even([1, 5]))
  7. Apply · Activity 7 of 7

    Mini-task. Write a function fahrenheit(celsius) that returns the temperature in degrees Fahrenheit: celsius * 9 / 5 + 32. Give it a one-line docstring. Then print the result for 100, -40 and 37.

    Check your work against this list

Build it yourself

Read the worked example, then write the exercises. Your code runs in your browser or on your computer and is never uploaded.

Worked example

A receipt built from two functions

line_total works out the cost of one line of a receipt and returns it. print_line prints a line and calls line_total to get the number. Because line_total returns its result, the program can also add two lines up. The last call shows what print_line gives back: it only prints, so the result is None.

main.py

def line_total(price, quantity):
    """Return the cost of one receipt line."""
    return price * quantity


def print_line(name, price, quantity):
    """Print one receipt line."""
    print(name, quantity, "x", price, "=", line_total(price, quantity))


print_line("apples", 0.5, 6)
print_line("bread", 2.25, 1)
total = line_total(0.5, 6) + line_total(2.25, 1)
print("total:", total)

result = print_line("milk", 1.1, 2)
print("print_line returned", result)

Run it with

python main.py

Output

apples 6 x 0.5 = 3.0
bread 1 x 2.25 = 2.25
total: 5.25
milk 2 x 1.1 = 2.2
print_line returned None
  • The two def blocks print nothing: the bodies run only when the functions are called.
  • line_total returns a number, so line_total(0.5, 6) + line_total(2.25, 1) can add the two results.
  • print_line has no return, so result is None even though the call printed a line.
  • Each function starts with a one-line docstring that says what it does.
Change it and run it

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.

Exercises

Exercise 1 of 2

Rectangle helpers

Complete two functions in main.py. area(width, height) returns width times height, and perimeter(width, height) returns the distance around the rectangle: two widths plus two heights. Both must return the number, not print it. The tests import them with from main import area, perimeter.

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.

Hints
  1. Hint 1

    Replace return 0 with the calculation; the parameters width and height already hold the numbers from the call.

  2. Hint 2

    The perimeter adds all four sides: width + height + width + height.

  3. Hint 3

    return width * height and return 2 * width + 2 * height.

Show a solution

One way to solve it. Yours can look different and still pass the checks.

def area(width, height):
    """Return the area of a rectangle."""
    return width * height


def perimeter(width, height):
    """Return the perimeter of a rectangle."""
    return 2 * width + 2 * height


print(area(3, 4), perimeter(3, 4))
Run it on your computer

Install Python 3.14 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.

main.py

def area(width, height):
    """Return the area of a rectangle."""
    return 0  # replace with the real calculation


def perimeter(width, height):
    """Return the perimeter of a rectangle."""
    return 0  # replace with the real calculation


print(area(3, 4), perimeter(3, 4))

test_main.py

from main import area, perimeter


def test_area():
    """area(3, 4) is 12"""
    got = area(3, 4)
    assert got == 12, f"area(3, 4) returned {got!r}, expected 12"


def test_area_other_sizes():
    """area works for 5 x 5 and 2.5 x 2"""
    got = area(5, 5), area(2.5, 2)
    assert got == (25, 5.0), f"area(5, 5) and area(2.5, 2) returned {got!r}, expected (25, 5.0)"


def test_perimeter():
    """perimeter(3, 4) is 14"""
    got = perimeter(3, 4)
    assert got == 14, f"perimeter(3, 4) returned {got!r}, expected 14"


def test_perimeter_square():
    """perimeter(5, 5) is 20"""
    got = perimeter(5, 5)
    assert got == 20, f"perimeter(5, 5) returned {got!r}, expected 20"

On macOS and Linux, type python3 wherever these commands say python, as in the first lesson.

Run the program:

python main.py

Run the checks (needs learnrun.py in the same folder):

python learnrun.py test
Download learnrun.py

Exercise 2 of 2

Letter grades

grade(score) should return a letter for a score from 0 to 100: "A" for 90 and up, "B" for 80 and up, "C" for 70 and up, and "F" below 70. The starter prints instead of returning, so every call gives None. Fix it with an if, elif and else chain that returns the letter.

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.

Hints
  1. Hint 1

    Replace print("F") with a chain of tests. Each branch ends with return and a letter.

  2. Hint 2

    Start with the highest grade: if score >= 90: return "A". Then elif score >= 80, and so on.

  3. Hint 3

    The else branch returns "F", so every score gets a letter and no call returns None.

Show a solution

One way to solve it. Yours can look different and still pass the checks.

def grade(score):
    """Return the letter grade for a score from 0 to 100."""
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    else:
        return "F"


print(grade(85))
Run it on your computer

Install Python 3.14 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.

main.py

def grade(score):
    """Return the letter grade for a score from 0 to 100."""
    # 90 and up: "A", 80 and up: "B", 70 and up: "C", below 70: "F".
    print("F")


print(grade(85))

test_main.py

from main import grade


def test_returns_the_letter():
    """grade(95) returns A"""
    got = grade(95)
    assert got == "A", f"grade(95) returned {got!r}, expected 'A' (does the function return the letter, or only print it?)"


def test_boundaries():
    """90, 80 and 70 are A, B and C"""
    got = grade(90), grade(80), grade(70)
    assert got == ("A", "B", "C"), f"grade(90), grade(80) and grade(70) returned {got!r}, expected ('A', 'B', 'C')"


def test_fail():
    """69 and 0 are F"""
    got = grade(69), grade(0)
    assert got == ("F", "F"), f"grade(69) and grade(0) returned {got!r}, expected ('F', 'F')"

On macOS and Linux, type python3 wherever these commands say python, as in the first lesson.

Run the program:

python main.py

Run the checks (needs learnrun.py in the same folder):

python learnrun.py test
Download learnrun.py

Common mistakes

Calling a function before its def has run

print(square(4))

def square(n):
    return n * n

What Python prints

NameError: name 'square' is not defined

Why, and the fix

Python runs a file from the top. When it reaches square(4), the def below has not run yet, so the name square does not exist. Put every def above the code that calls it; usually all functions go at the top of the file.

Leaving out an argument

def greet(name):
    print("Hello,", name)

greet()

What Python prints

TypeError: greet() missing 1 required positional argument: 'name'

Why, and the fix

greet has one parameter, so every call needs one argument: greet("Ada"). The message names the parameter that got no value. The next lesson shows how a default value makes an argument optional.

Printing instead of returning

def double(n):
    print(n * 2)

result = double(5) + 1

What Python prints

TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

Why, and the fix

double shows 10 but returns nothing, so double(5) is None, and None + 1 is an error. NoneType is the type of None. Write return n * 2, and print the result where you call the function if you want to see it.

Python in the browser: Pyodide 314.0.7, MPL-2.0. Licence and source

Exit ticket

5 questions, no hints. Score 80% or more to complete the lesson.

Finish every activity above to unlock the exit ticket.

Report a problem

Spotted something wrong or unclear? Say what, and it will be checked and fixed.

#

At least 20 characters.

Only if you want a reply.

Key ideas

def names a block of code; a call runs it

def area(width, height): creates a function called area with two parameters. The indented lines under it are the body. Running the def line does not run the body: it only binds the name to a function. The body runs each time you call the function with parentheses, area(3, 4). The values in the call, 3 and 4, are the arguments; they are assigned to the parameters in order. The name alone, area without parentheses, is just the function object, and calls nothing.

return hands a value back

return width * height ends the function and hands the value to the caller, so area(3, 4) can be stored, printed or used in a sum. print only shows text on the screen; the caller gets nothing from it. A function that ends without return, or with a bare return, returns None. That is why double(5) + 1 fails when double prints instead of returning: Python tries None + 1. return also stops a loop inside the function at once.

Local variables and docstrings

Variables assigned inside a function, parameters included, are local: they exist only while that call runs, and they do not change a variable of the same name outside. A function can still read a variable from outside; it sees the value it has when the call happens. If the first line of the body is a string, it is the docstring, stored in area.__doc__. Its first line is a short summary that starts with a capital letter and ends with a period.

Sources

Last reviewed September 29, 2026