On this page

Functions

Every program so far has been one list of instructions, run from the top. A function is a group of instructions with a name. You write it once, and from then on the name is an instruction of your own, as usable as circle or print.

Before this, you can: write a loop that uses its loop variable; write an if with and / or; tell what a program prints by tracing it.

By the end you will be able to define a function and call it, pass values in and hand a value back, tell showing a value from returning it, say which names live inside a function, build one function out of others, and write a function that runs when the picture is clicked.

Before you read on, decide what this prints:

def total(a, b):
    a + b

print(total(1, 2))
None

If you expected 3, you may know a language where the last value worked out in a function is what it gives back. In Python a function gives back only what a return line hands over. This one works out 3 and then drops it, so the call gives back None, Python's word for "no value". Nothing warns you. This module comes back to that twice, because it is one of the most common mistakes in Python.

Your own instruction

Here is a function and two calls of it:

def cheer():
    print("Hip hip")
    print("Hooray")

cheer()
cheer()
Hip hip
Hooray
Hip hip
Hooray

The line starting with def gives the function its name. The indented lines under it are its body: what the function does. cheer() further down is a call: it runs the body, and when the body is finished the program carries on from the line after the call.

The round brackets matter. cheer is the name of the function; cheer() runs it.

Define is not do

A def does not run the body. It only teaches Python the name. Nothing in the body happens until a call.

def cheer():
    print("Hip hip")
    print("Hooray")

print("Ready")
cheer()
Ready
Hip hip
Hooray

Ready comes first, although the two print lines of the body are higher up the page. Python read the def, remembered the body, and moved on. A program with a def and no call of it prints nothing from that function at all.

One consequence: a function has to be defined before the line that calls it is run. Put your def lines near the top.

Values in

A function is more useful when each call can be different. The names in the brackets of the def are parameters. A call puts a value into each one, in order.

from scimigo import canvas, circle, rectangle

canvas(600, 400)

def draw_tree(x, height):
    rectangle(x - 8, 320 - height, 16, height, color="saddlebrown")
    circle(x, 320 - height, 40, color="forestgreen")

draw_tree(120, 100)
draw_tree(300, 180)
draw_tree(480, 60)

In the first call x is 120 and height is 100. In the second they are 300 and 180. The body is the same three times; only the values differ.

You have been doing the other half of this since the first module. circle(x, 200, 40, color="forestgreen") is a call that passes values in. A value can also be passed by the parameter's name, as color= is, and then the order does not matter:

def frame(width, height):
    return width - height

print(frame(5, 2))
print(frame(height=2, width=5))
3
3

Python has no declared types for parameters. draw_tree("tall", 100) is accepted as a call and fails only when the body tries to subtract 8 from "tall".

A value out

print shows a value to the person at the screen. return hands a value back to the program, at the place where the function was called.

def double(n):
    return n * 2

print(double(4) + 1)
print(double(double(3)))
9
12

Read double(4) + 1 as: run double with n = 4, and put what it returns, 8, in place of the call. Then 8 + 1 is 9. In the second line the inner call returns 6, and that 6 is what the outer call receives.

return also ends the function at once. Lines after it in the body do not run:

def entry_fee(age):
    if age < 5:
        return 0
    print("checking", age)
    return 12

print(entry_fee(3))
print(entry_fee(30))
0
checking 30
12

For entry_fee(3) the function stops at return 0, so checking is never printed. That makes a rule with several cases easy to write: return as soon as you know the answer.

Showing is not returning

These two functions look alike and are not.

def area_shown(w, h):
    print(w * h)

def area(w, h):
    return w * h

first = area_shown(2, 3)
second = area(2, 3)
print(first, second)
6
None 6

area_shown puts 6 on the screen and hands nothing back, so first is None. area shows nothing and hands 6 back, so second is 6. The program can go on to use second. It cannot use first as an area, to add it or multiply it.

A function that reaches the end of its body without a return gives back None. That is not an error by itself. The error comes later, when the program tries to use the None as a number:

def area_shown(w, h):
    print(w * h)

total = area_shown(2, 3) + area_shown(1, 4)
  File "your_program.py", line 4
    total = area_shown(2, 3) + area_shown(1, 4)
TypeError: unsupported operand type(s) for +: 'NoneType' and 'NoneType'

Both calls run and show their numbers, and then the + fails. Whenever an error mentions NoneType, look for a function that shows its answer, or works it out, and never returns it.

Which should a function do? If the rest of the program needs the value, return it. Let the main program decide whether to print it.

Names that stay inside

A name that is given a value inside a function belongs to that call of the function. It appears when the call starts and is gone when the call ends.

x = 10

def change():
    x = 99
    print("inside", x)

change()
print("outside", x)
inside 99
outside 10

There are two different names here that happen to be spelt the same. The x inside change is made by the call and thrown away after it. The x outside is never touched.

Parameters are names inside the function too. Giving a parameter a new value changes nothing outside:

def add_one(n):
    n = n + 1

score = 5
add_one(score)
print(score)
5

The call copies the value 5 into n. The function changes its own n to 6 and then ends, and that n is gone. score was never involved. To get the new value out, the function has to return it and the caller has to keep it: score = add_one(score), with return n + 1 in the body.

When you step through a program in the lab, each call gets its own box of names, which appears at the call and disappears at the return. Watch it once and this section becomes obvious.

Small functions make big ones

A function can call another function. That is how large programs are built: small pieces with clear names, used by slightly bigger pieces.

def tax(price):
    return price // 5

def price_with_tax(price):
    extra = tax(price)
    return price + extra

print(price_with_tax(50))
60

Follow the one call on the last line. price_with_tax starts with price = 50. Its first line calls tax, so price_with_tax waits while tax runs with its own price, returns 10, and ends. Now extra is 10, and price_with_tax returns 60 to the last line, which prints it.

Each call has its own names. The two functions both have a price, and they do not interfere.

The same idea draws pictures. A function that draws one tree can be used by a function that draws a row of them:

from scimigo import canvas, circle, rectangle

canvas(600, 400)

def draw_tree(x, height):
    rectangle(x - 8, 320 - height, 16, height, color="saddlebrown")
    circle(x, 320 - height, 40, color="forestgreen")

def draw_row(count, height):
    for i in range(count):
        draw_tree(70 + i * 115, height)

draw_row(5, 140)

draw_row knows nothing about rectangles and circles. It only knows that draw_tree draws a tree.

A function you never call yourself

So far your program has made every call. A function can also be handed to something else, which calls it later. on_click takes a function and remembers it. Each time the picture is clicked, the lab calls that function with the position of the click.

from scimigo import canvas, circle, on_click

canvas(600, 400)

def clicked(x, y):
    circle(x, y, 10, color="blue")

on_click(clicked)
print("waiting for a click")
waiting for a click

Notice on_click(clicked), with no brackets after clicked. You are handing over the function itself, not calling it. on_click(clicked()) would call clicked straight away, with no position, and fail.

When this program is run it draws nothing: the body of clicked has not run, because nothing has called it. Then you click at, say, (120, 80), and the lab calls clicked(120, 80). A second click calls it again with new values. The function has to take exactly two parameters, because two values are passed in.

In the lab

You will put the lines of a program in the order they run, finish a flower function and plant a garden with it, make a function return a width, decide when the last line of a function runs, tell showing from returning, follow one function calling another, and then write functions that the picture calls when you click it.

Your turn

Now try it yourself

Predict what a program will do, run it, and step through it line by line. The checks tell you when your own code works.

Loading the lab…

In this lab

  1. Define is not do

    This program defines a function, prints a line, and then calls the function twice. Decide the order in which things are printed before you run it.

  2. Flower

    draw_flower(x, y, color) should draw a flower centred at (x, y). So far it draws one petal, above the centre. Finish it:

    • Add three more petals, each a circle of radius 18 in color: one 25 below the centre, one 25 to the left, one 25 to the right.
    • Then draw the centre last, so it lies on top: a circle of radius 14 at (x, y), coloured "gold".

    Leave the two calls at the bottom as they are. The checks also call your function with a position and a colour of their own.

  3. What comes back?

    Four short programs. In each, work out what is printed before you run it. double returns a value; shout only shows one.

  4. Badge width

    Each name badge should be as wide as its name needs. badge_width(letters) is meant to work that out, but it returns 40 whatever it is given, so both badges are the same width.

    Change the function so that it returns 40 plus 14 for every letter. Do not change the lines below it.

  5. Does the last line run?

    This function has a return inside an if, and more lines after it. Decide what each call prints and hands back.

  6. Showing or returning?

    Three versions of area. One shows its answer, one returns it, and one does neither. Decide what each program does.

  7. Whose x?

    Three short programs about which names a function can see and change. Decide what each one does.

  8. Who calls whom?

    price_with_tax uses tax. Follow the one call on the last line, and say which lines run in which order. Stepping through it afterwards shows each call's own names.

  9. Garden

    draw_flower is finished. Use it to write a second function, draw_garden(count, y, color), which plants count flowers in a row:

    • the first flower is at x = 80, and each one after it is 100 further right;
    • every flower is at height y and has colour color.

    Write the function where the comment is. The two calls at the bottom then plant two rows. The checks also call your function with values of their own.

  10. Click to draw

    on_click(clicked) asks the lab to call clicked each time the picture is clicked, with the position of the click. At the moment clicked only prints that position.

    Run the program and click the picture a few times to see it. Then change clicked so that every click leaves a blue dot of radius 10 exactly where it landed. You may keep the print line or remove it.

  11. Target game

    The gold rectangle is a target. Its left edge is at x = 200 and its right edge at x = 400; its top is at y = 120 and its bottom at y = 280.

    1. Write inside(x, y). It returns True when the point (x, y) is in the target, edges included, and False otherwise. It draws and prints nothing.
    2. Write clicked(x, y). It draws a circle of radius 8 at the click: "green" if inside(x, y) is True, "red" if not.
    3. Hand clicked to on_click.

    Then play: click inside and outside the target.

  12. Ticket price, as a function

    In Decisions you worked out a ticket price from age and is_student. Turn that rule into a function, ticket_price(age, is_student), which returns the price and prints nothing:

    • children under 5 go free: 0;
    • anyone under 16 pays 6, and so does a student of any age;
    • everyone else pays 10.

    Then use it. Under your function, print the prices for these four customers, one per line, in this order: age 3, not a student; age 12, not a student; age 20, a student; age 40, not a student.

  13. Labelled rectangles

    Write three functions.

    • area(w, h) returns the area of a rectangle w wide and h tall.
    • perimeter(w, h) returns the distance all the way round it.
    • draw_labelled(x, y, w, h) draws a "teal" rectangle with its top-left corner at (x, y), then writes two numbers under it with text: the area at (x, y + h + 25), and the perimeter at (x, y + h + 50). It must get both numbers by calling your other two functions.

    Then call draw_labelled twice: once with (40, 40, 200, 100) and once with (320, 40, 120, 180).

  14. The total that is None

    This program should add two totals and print the answer on one line: 10. Instead it stops with an error.

    Run it and read the message. It names a type you did not write anywhere. Decide what the message is telling you, then fix the function so that the program prints only 10. Do not change the last line.

  15. The score that will not change

    add_point is supposed to add one to the score. It is called twice, and the program still prints 0.

    Before you change anything, say to yourself which score the line inside the function changes. Then fix the program so that it prints 2: the function must hand the new score back, and the main program must keep what it gets.

  16. Stamp tool

    Make a stamp tool. Design a small shape of your own (a star, a house, a face, a leaf) and write it as a function that takes a position and a colour. Then make every click stamp it where the click landed: one colour for clicks on the left half of the canvas, another for clicks on the right half.

    There are no checks. When it works, go through the list below.