Functions are one of the most important concepts in this course, so this chapter is dedicated to them. Some people call functions the “atomic unit of a program”, since programs are built out of functions. You can think of a function as a miniature program with its own input, processing, output workflow: a reusable chunk of code.
# The `input` function
height = input("How tall are you, in inches?")
# The `int` function
height = int(height)
# The `round` function
feet = round(height / 12)
# The `print` function
print("You are", feet, "feet tall, rounded to the nearest foot.")
The first part of this chapter is about calling functions, which is how functions are used. When you call a function, you put parentheses after its name. Sometimes you also provide arguments: values that go inside the parentheses. You might think of a function as a mysterious box you cannot see inside of. The arguments are slots on top of the box that you put data into; more data comes out of the bottom, which is the returned value. That value is substituted back into the program, just like the result of any other expression. Python has many built-in functions, some of which you already know: input, int, round, and print are four examples shown here.
def average(num1: int, num2: int) -> int:
"""
Calculate the average of two integers, returning an integer.
"""
return (num1 + num2) // 2
result = average(5, 10)
print(result)
The next part of this chapter is about how to define your own functions. The programmer must use the def keyword and provide a name for the function, in this example average. The programmer must also give formal names, called parameters, for the expected arguments, along with their expected types; these become variables the function can use. The line with def is the header of the function, and the indented lines below it are its body. The first line of the body is often a docstring, a string literal that explains the function’s purpose. The rest of the body is the instructions that run when the function is called, ending with a return statement. Once defined, a function is called just like a built-in one.
from bakery import assert_equal
def average(num1: int, num2: int) -> int:
"""
Calculate the average of two integers, returning an integer.
"""
return (num1 + num2) // 2
assert_equal(average(5, 10), 7)
assert_equal(average(0, 0), 0)
assert_equal(average(1, 3), 2)
Writing a function does not mean it works correctly. A whole section is dedicated to how functions must be unit tested to try to prove their correctness. A unit test shows an example input and the expected output for when the function is working correctly. This course uses a library named bakery, with a function assert_equal you can use to test your programs. Going forward, you’ll be expected to test all of your functions.
Programs get significantly more complicated from here on, so this chapter also covers strategies professional programmers use to figure out why a program isn’t working: talking to rubber ducks, “fencing in” a bug by narrowing down where it happens, and the scientific method of guessing and checking.
# num1 and num2 are local variables for average
def average(num1: int, num2: int) -> int:
return (num1 + num2) // 2
# my_height, your_height, and averaged_height are global variables
my_height = 40
your_height = 62
averaged_height = average(my_height, your_height)
The indented region under a function’s header is its body — every program has at least one body (its own), and every function has its own body too. A related idea is scope: a body that controls how long a variable lives. A variable inside a scope is only available inside that scope; once the scope ends, the variable is gone. There is also a global scope for the whole program, but you should avoid using global variables inside your functions.
def average(num1: int, num2: int) -> int:
"""
Calculate the average of two integers, returning an integer.
"""
The first line of a function’s body should always be a string literal: specifically, a triple-quoted string with specially formatted contents, called a docstring. It documents the function’s purpose for other programmers. Each line should be indented to match the function’s body. A docstring only counts if it is the first line of the body — not written before or after the function.
def add5(number: int) -> int:
result = number + 5
return result
def double_and_add5(value: int) -> int:
answer = 2 * add5(value)
return answer
final = double_and_add5(7)
print(final)
Data flows through a program from its inputs to its outputs, being transformed along the way. So far, that flow went from the top of the program to the bottom. Once functions call other functions, execution jumps around according to the rules of calls and returns, so it becomes critical to track what data is available at each step.
from drafter import *
@route
def index() -> Page:
return Page(Div(
italic("This"),
" is ",
bold("exciting!"),
))
start_server()
Python has a lot of useful built-in functions, but you will often need even more. Just like variables, functions can be defined in other modules and then imported. The last section of this chapter uses a web-app library named drafter to build a page like the one above; later chapters and activities cover Drafter in more depth.