A mobile wallet app has many screens: paying a shop by QR code, recharging a phone, paying the electricity bill, sending money to a friend. Every one of them ends with the same few steps: check the PIN, check that there is enough money, move it, show a receipt. Nobody typed those steps out again for every screen. They were written once, given a name, and every screen that needs them uses them by that name. The programs on your phone are built like this, from small parts, each with a name and one job.
In Python such a part is called a function: a named set of lines that does one job. (Unit 2 called it a procedure, the general name, and Unit 3 drew it in a flowchart as a procedure box.) You have used functions since Unit 1: print(), input() and int(), then range() in Unit 2 and len() in Unit 3, are functions that other people wrote. In this unit you write your own. You will break a big job into parts and make each part a function, give a function the values it needs, get an answer back from it, and use it again and again. You will turn Unit 3’s search and sort into functions, finish the quiz you built in Grade 7, and learn to read the documentation that tells you how someone else’s function works.
Big jobs, small parts
In Grade 6 you drew a house with the turtle. You didn’t start dragging blocks straight away: you split the house into a square and a roof, and built one part at a time. Breaking a big job into smaller parts, each easy to do, is decomposition. You met the word in Grade 6, and in Grade 7 you saw flood forecasters and nurses use it.
Decomposition doesn’t stop after one split. A part that is still too big is split again, until every part is small enough to do in a few lines. Here is a street, split twice:
Look at what the split shows. The street needs a house three times and a tree twice. A house and a tree both need a rectangle. If each part has a name and is written once, a program can use it as often as it likes: draw a house here, another there, a bigger one at the end. That named part, written once and used many times, is a function.
Real apps are split the same way. In Grade 7 you saw a bus ticket app split into its jobs: find a bus, choose a seat, pay, get the ticket. Each job is split again, and each small part becomes a function, often written by a different programmer.
What is decomposition?
Pick an answer to see why.
In the street’s decomposition, why is it worth writing house as one named part?
Pick an answer to see why.
Your first function
At Tihar, groups go from house to house singing Deusi. One singer sings a line, often made up on the spot, and everyone answers with the same words: Deusi re! The lines change from house to house, but nobody needs the answer written out after each one: everyone knows it, and it comes whenever it is needed. A function works the same way: you write its lines once, give them a name, and then the name alone stands for all of them.
def chorus(): print("Deusi re!") print("We have come to your door,")chorus()print("on this night of Tihar.")chorus()Output
We have come to your door,Deusi re!on this night of Tihar.Deusi re!There are two parts to using a function.
Defining it says what it does. def (short for define) starts the definition, then comes the function’s name, chorus, then round brackets and a colon. The lines indented under it are the function’s body, the lines it runs, just as a loop has a body (Unit 2); here there is just one. Function names follow the same rules as variable names, and like them should say what is inside: a function does something, so its name is often a verb, such as draw_house or find_contact.
Calling it makes it run. A call is the name with round brackets after it: chorus(). Python jumps to the function, runs its body from the top, and when the body ends it comes back to the line after the call and carries on.
So a definition alone runs nothing. Python reads lines 1 and 2, remembers that chorus means that print, and goes straight on to line 4. Only the calls, on lines 5 and 7, make the group answer.
A house, in parts
Here is Grade 6’s house again, as blocks and as Python. In Python each part has a name of its own: walls and roof.
The last two lines are the whole plan: draw the walls, then the roof. Anyone reading the program can see what it does from those two lines, before reading how each part is done. That is one of the best things about functions: the names tell the story.
A program defines a function with def greet(): but never writes greet(). What happens when it runs?
Pick an answer to see why.
Which line calls a function named draw_star?
Pick an answer to see why.
Parameters: what a function needs
A square function that always draws a square of side 80 is not much use when you want a small square and a big one. In Grade 6 you changed the number in the move block. A function can take that number from the line that calls it:
import turtlet = turtle.Turtle() def square(size): for i in range(4): t.forward(size) t.right(90) square(40)square(80)square(120)turtle.done()size in def square(size): is a parameter: a variable that gets its value from the call. In square(40), the 40 is an argument: the value given to the function when it is called. When square(40) runs, Python puts 40 into size before the body starts, so every t.forward(size) moves 40 steps. The next call puts 80 into size, and the next 120. One definition, three different squares.
A function can have several parameters, separated by commas. The arguments go into them in order: the first argument into the first parameter, the second into the second. Here is the tea-shop bill from Unit 1, as a function:
def bill(name, cups, samosas): total = cups * 30 + samosas * 25 print(f"{name}, please pay Rs {total}.") bill("Mina", 2, 3)bill("Pemba", 1, 0)Output
Mina, please pay Rs 135.Pemba, please pay Rs 30."Mina" into name, 2 into cups, 3 into samosas. A function’s variables are its own
The parameters, and any variable a function sets inside its body, such as total, belong to that function. They are made when it is called and thrown away when it ends. So the rest of the program can’t see them:
def bill(name, cups, samosas): total = cups * 30 + samosas * 25 print(f"{name}, please pay Rs {total}.") bill("Mina", 2, 3)print(total)Error message
Traceback (most recent call last): File "main.py", line 6, in <module> print(total) ^^^^^NameError: name 'total' is not definedThat is a good thing. Inside its function, total can’t clash with another variable called total in another function or in the main program. A programmer can write a function without knowing every name used everywhere else in a big program. To get a value out of a function, there is a better way, in the next section. (Grade 10 explains these rules in full: they are called a variable’s scope.)
In def area(length, width): and the call area(5, 8), which are the arguments?
Pick an answer to see why.
def greet(name, times): and the call greet(3, "Asha"). What is in name while the function runs?
Pick an answer to see why.
Return: a function that answers
Some functions do something: print() shows text, t.forward() moves the turtle, bill() prints a bill. Others work out something and give the answer back to the line that called them: len(marks) gives back the number of items, int("12") gives back the number 12, round(7.456, 1) gives back 7.5. The answer is called the function’s return value, and the call is replaced by it, as if you had written the value there. That is why cups = int(input("Cups? ")) works: input() gives back the text typed, int() gives back a number made from it, and = puts that number into cups.
Your own function gives back an answer with return:
def bill(cups, samosas): return cups * 30 + samosas * 25 mina = bill(2, 3)pemba = bill(1, 0)print("Mina pays Rs", mina)print("Together: Rs", mina + pemba)Output
Mina pays Rs 135Together: Rs 165return does two things: it gives the value after it back to the call, and it ends the function at once. Any line of the function after a return that runs is never reached.
That makes a grade calculator neat. Here is Unit 2’s SEE grade table as a function that answers with the grade:
def grade_for(marks): if marks >= 90: return "A+" elif marks >= 80: return "A" elif marks >= 70: return "B+" elif marks >= 60: return "B" elif marks >= 50: return "C+" elif marks >= 40: return "C" elif marks >= 35: return "D" else: return "NG" mina = grade_for(85)print("Mina:", mina)pemba = grade_for(34)print("Pemba:", pemba)Output
Mina: APemba: NGBecause a function that returns doesn’t print, the program that calls it decides what to do with the answer: print it, keep it, add it up, compare it. grade_for() can be used in a report card, a class list or a results website, each showing the grade its own way.
What does this print?
def double(n): return n * 2 print("Done") print(double(4) + 1)Pick an answer to see why.
A function has no return line. What does a call to it give back?
Pick an answer to see why.
Write once, use many times
Now Grade 7’s promise. Your quiz in blocks asked each question with the same few blocks, copied and changed. As a function, a question is one line:
def ask(question, answer): reply = input(question + " ") if reply == answer: print("Correct!") return 1 else: print("Sorry, the answer is", answer) return 0 score = 0score = score + ask("What is the capital of Nepal?", "Kathmandu")score = score + ask("How many districts does Nepal have?", "77")score = score + ask("What is 7 × 8?", "56")print(f"Your score: {score} out of 3")Output
What is the capital of Nepal? KathmanduCorrect!How many districts does Nepal have? 75Sorry, the answer is 77What is 7 × 8? 56Correct!Your score: 2 out of 3ask returns 1 for a right answer and 0 for a wrong one, so adding up what it returns counts the right answers. A new question is one more line. And when you improve ask, every question gets better at once: the program has to change in one place only.
Here is an improvement it needs. In Unit 1, the quiz said Sorry to kathmandu typed in small letters, because to Python "k" and "K" are different characters. Any string can give you a copy of itself with every capital letter made small: write .lower() after it.
reply = "KathMandu"print(reply.lower())print("SEE Results 2082".lower())Output
kathmandusee results 2082.lower() is a function that belongs to strings, so it is written after the string, with a dot (Grade 10 explains functions like this). It doesn’t change reply itself: it gives back a new string. Digits, spaces and Nepali letters, which have no capitals, stay as they are. Compare two strings after .lower(), and capital letters no longer matter.
Unit 3’s search and sort, as functions
In Unit 3’s project you drew SORT the list and binary search for the name as procedure boxes. Here they are as real functions. A function can take a whole list as an argument, so the same function can search any list:
def linear_search(items, target): for i in range(len(items)): if items[i] == target: return i return -1 classmates = ["Sita", "Bikash", "Gita", "Hari"]teachers = ["Thapa sir", "Gurung miss", "Shrestha sir"]print(linear_search(classmates, "Gita"))print(linear_search(teachers, "Gurung miss"))print(linear_search(classmates, "Asha"))Output
21-1This is shorter than Unit 3’s linear search, because return can leave the loop as soon as the target is found: no found flag is needed. If the loop gets to the end without finding it, the line after the loop returns −1. A search that finds something returns its position, 0 or more, so −1 can never be mistaken for one: programmers use it as a signal that means “not found”. The program that calls the function checks for it.
Binary search and bubble sort turn into functions the same way. The bodies are Unit 3’s algorithms, with the list and the target as parameters:
def bubble_sort(items): swapped = True while swapped: swapped = False for i in range(len(items) - 1): if items[i] > items[i + 1]: temp = items[i] items[i] = items[i + 1] items[i + 1] = temp swapped = True def binary_search(items, target): low = 0 high = len(items) - 1 while low <= high: middle = (low + high) // 2 if items[middle] == target: return middle elif items[middle] < target: low = middle + 1 else: high = middle - 1 return -1 names = ["Pemba", "Aarati", "Sunita", "Hari", "Yamuna", "Bikash"]bubble_sort(names)print(names)print(binary_search(names, "Sunita"))print(binary_search(names, "Kiran"))Output
['Aarati', 'Bikash', 'Hari', 'Pemba', 'Sunita', 'Yamuna']4-1Two things are worth noticing.
bubble_sorthas noreturn. It changes the list it is given: items inside the function is not a copy, it is the list names, under a second name. So afterbubble_sort(names), names is sorted. (A number or a string given as an argument can’t be changed like that; a list can.)- The main program is now four short lines that say what happens: sort the names, print them, search twice. How each job is done is in its function, written and tested once.
Test each function on its own
A function is a small program with a clear job, so it can be tested alone, with Unit 3’s test tables, before the rest of the program is written. Call it with inputs whose answers you know, and compare:
def binary_search(items, target): low = 0 high = len(items) - 1 while low <= high: middle = (low + high) // 2 if items[middle] == target: return middle elif items[middle] < target: low = middle + 1 else: high = middle - 1 return -1 numbers = [3, 8, 12, 17, 21]print(binary_search(numbers, 12) == 2)print(binary_search(numbers, 3) == 0)print(binary_search(numbers, 21) == 4)print(binary_search(numbers, 10) == -1)print(binary_search([], 5) == -1)Output
TrueTrueTrueTrueTrueEach line is one test: an ordinary one, the first item, the last item, a number that isn’t there, and an empty list. A False would show at once which test failed. Professional programmers keep tests like these beside their functions and run them all again after every change, so a fix in one place can’t quietly break another.
Why functions?
Put together, functions give a program five things:
- Reuse. Written once, used as often as needed, in this program or the next one.
- One place to fix. A bug in a function is fixed once, for every call.
- Testing in small pieces. Each function can be tested alone, before the whole program exists.
- Readable programs.
bubble_sort(names)says what happens; the reader doesn’t have to work it out from twelve lines. - Sharing work. Different people can write different functions, as long as they agree what each one needs and what it gives back. Python’s own functions are exactly that: people you will never meet wrote
print()andsorted()for you.
linear_search(["Ram", "Sita"], "Gita") returns −1. What does that mean?
Pick an answer to see why.
A bug is found in a function that a program calls in 20 places. How many places must be fixed?
Pick an answer to see why.
Reading documentation
You can’t read the code of every function you use: Python’s own functions are built into it, and many are thousands of lines long. You don’t need to. Every function made for others to use comes with its documentation (docs for short): what it does, what to give it, and what it gives back. Reading documentation is one of a programmer’s most important skills, and the professionals do it every day.
help()
Python carries short documentation for its functions inside itself. help() shows it. Press Run:
help(round)Output
Help on built-in function round in module builtins: round(number, ndigits=None) Round a number to a given precision in decimal digits. The return value is an integer if ndigits is omitted or None. Otherwise the return value has the same type as the number. ndigits may be negative.round(). The first line shows how to call it; the rest says what it does and what it gives back. Read an entry in this order:
- The first line shows how to call the function: its name and its parameters, in order. Here
roundtakes number and ndigits. - A parameter with
=and a value is optional: you may leave it out, and then it has that value, its default.ndigits=Nonemeansround(7.456)is allowed, and then ndigits isNone. - The description says what it does, and what it returns: here, a whole number if ndigits is left out, and otherwise a number of the same type as the one given.
- Try it in a tiny program to check you understood:
print(round(7.456))print(round(7.456, 1))print(round(7.456, 2))Output
77.57.46Without ndigits, round() gives the nearest whole number; with it, that many decimal places. What would round(1234, -2) give? The entry says ndigits may be negative. Try it.
The official documentation
help() is short. The full documentation of Python is a website, with a page for each module, an entry for each function, and examples. Here is part of its entry for the turtle’s circle():
docs.python.org: turtle.circle
turtle.circle(radius, extent=None, steps=None) radius – a number extent – a number or None steps – an integer or None Draw a circle with the given radius. The center is radius units leftof the turtle; extent, an angle, determines which part of the circleis drawn. If extent is not given, draw the entire circle.The documentation writes turtle.circle, but it works on any turtle, so with ours it is t.circle(…). Reading the entry: radius is needed; extent and steps are optional. Leave out extent and you get a whole circle. Give it an angle, such as 180, and only that much of the circle is drawn: half of it. The center is to the turtle’s left, so a turtle facing right draws its circle above itself.
import turtlet = turtle.Turtle()t.circle(50)t.penup()t.forward(150)t.pendown()t.circle(50, 180)turtle.done()Write documentation for your own functions
Your own functions can carry documentation too. A string in triple quotes, """…""", on the first line of a function’s body is its docstring. Python stores it, and help() shows it:
def paisa(rupees): """Return the number of paisa in that many rupees.""" return rupees * 100 help(paisa)Output
Help on function paisa in module __main__: paisa(rupees) Return the number of paisa in that many rupees.A good docstring says what the function does, what each parameter should be, and what it gives back, in a sentence or two. Write it for a stranger, or for yourself in six months: by then you will have forgotten too.
The documentation says power(base, exp, mod=None). Which call is not allowed?
Pick an answer to see why.
What does a docstring do?
Pick an answer to see why.
Plan with functions
Put the unit together. For a program of your own:
- Decompose. Split the job into parts, and the parts into smaller parts, as in the street. Look for parts that are needed more than once.
- Name each part. A function’s name says what it does:
draw_house,find_contact,grade_for. - Decide what each part needs and gives back. What it needs become its parameters; an answer it works out is its return value. Write the docstring now, before the body: if you can’t say what the function does in a sentence, split it again.
- Write and test one function at a time, with a few test calls whose answers you know.
- Write the main program last: a few lines that call the functions in order.
In a flowchart, a call to a function is a procedure box, as in Unit 3, and each function gets a flowchart of its own. It starts with the function’s name and its parameters in place of Start, and ends with Return in place of Stop:
In short
Exercise
Which line starts the definition of a function?
Pick an answer to see why.
What does this print?
def show(n): print(n * 3) show(2)show(5)Pick an answer to see why.
def bill(cups, samosas): is called as bill(3). What happens?
Pick an answer to see why.
What is in x after this program?
def add(a, b): print(a + b) x = add(2, 3)Pick an answer to see why.
A program calls linear_search(names, "Hari") and gets 0. What does that mean?
Pick an answer to see why.
Documentation says text_box(message, width=40, border=True). How many arguments must a call give?
Pick an answer to see why.
What is decomposition? Show how you would decompose the job “make a birthday card program that draws a cake and writes a message”, at least two levels deep, and say which parts you would make functions with parameters.
Answer
Explain the difference between a parameter and an argument, using def area(length, width): and area(5, 8).
Answer
Write the output of this program, and explain the order in which its lines run.
def greet(name): print("Namaste,", name) print("Welcome!") print("Start")greet("Dolma")greet("Bishal")print("End")Answer
What is the difference between print and return in a function? Write two versions of a function square_of(n), one with each, and show which one can be used in total = square_of(3) + square_of(4).
Answer
Write a function is_even(n) that returns True if n is even and False if it is odd. Use it in a loop to print the even numbers from 1 to 10.
Answer
A student wrote this to print the area of a rectangle plus 10, but it prints 40 and then stops with an error. Explain both, and correct the program.
def area(length, width): print(length * width) a = area(5, 8)print(a + 10)Answer
Write a function highest(marks) that returns the highest number in a list, using Unit 2’s “highest so far” pattern, not Python’s max(). Test it with three lists, including one with a single item.
Answer
Here is the documentation of a function in a module for drawing Nepal’s flag (a made-up example): draw_flag(height, x=0, y=0, outline=True). “Draws Nepal’s flag, height units tall, with the bottom of its pole at (x, y). If outline is False, the blue border is left out. Returns the flag’s width.” Answer:
- i.Which parameters must be given, and which are optional?
- ii.Write a call that draws a flag 200 units tall at (−100, −50).
- iii.Write a call that draws one 100 tall at (0, 0) without the border, and keeps its width in a variable.
Answer
A program for a shop prints a receipt line in seven places, each time with the same four lines of code but different item names and prices. Explain how a function would make the program better, giving at least three reasons.
Answer
Draw the flowchart of a function bigger(a, b) that returns the bigger of two numbers, and of a main program that inputs two numbers, calls it, and outputs the answer.
Answer
Quiz yourself: Algorithms and programming, Grades 6–12 →
Practice every question in the book, and keep track of the ones you miss →
