Week 3: Functions

Reading: Think Python Chapter 3

Notes

Function Calls

Functions are pre-defined sequences of instructions. Like variables, functions have names, and functions must adhere to the same naming convention. We can call a function with the following syntax:

function_name()
  • The function’s name is spelled out
  • A set of parentheses comes immediately after the function name
    • Values placed in the parentheses, called arguments, modify what the function does
  • After the function call finishes, the function call is replaced with a return value
    • Not all functions have a return value
    • If there is no return value, then a special object None is returned

Unless you define a function called function_name() when you run the code above, you’ll get an error.

Functions can form parts of expressions:

int('25') + 2
27

Since int('25') returns the integer 25, after the function call, this is equivalent to:

25 + 2
27

Expressions can form the arguments of function calls:

int('2' + '5') + 2
27

The expression '2' + '5' is evaluated before the function call. It is inside the parentheses of the call, and function call parentheses respect order just as expression parentheses do.

'2' + '5'
'25'

After the evaluation, the result becomes int('25') + 2, identical to the first example. As always, these intermediate steps will never be shown to us by Python, but it’s important to understand how the programming language works.


There is no limit to how deep you can nest function calls and expressions:

str(int('2' + '5') + 2) + "3"
'273'

In practice, however, writing and reading this sort of deeply-nested function call is difficult, and unnecessary. Instead, use variables:

first = '2' + '5'
second = int(first) + 2
third = str(second) + "3"
print(third)
273

Math

Python comes with a built-in module, math, for simple mathematical functions. The module comes with functions and variables.

To import a module, use the import keyword, followed by a space, followed by the module name:

import math

You only need to do this once in the interpreter, or once at the beginning of a program you write in the editor.

After importing the module, you can use its functions:

math.ceil(2.1) # math.ceil returns the next largest integer
3
math.floor(1.99) # math.floor returns the next smallest integer
1

Note the syntax: there is a dot in between the module name math and the name of the function: this indicates to Python that the function is associated with the module.

Modules can also include variables; one example from math is math.pi, a good approximation of \(\pi\):

math.pi
3.141592653589793

Composition

Like everything you’ve seen so far, these functions and variables can be composed into expressions. We’ll start with a simple expression:

math.pi/2
1.5707963267948966

This expression can form the argument of a function:

math.ceil(math.pi/2)
2

That expression can be combined with other functions or operators into a more complicated expression:

math.ceil(math.pi/2) + math.floor(3 + int('1'))
6

There’s no technical limit to how complicated this can get:

str(math.ceil(math.pi/2) + math.floor(3 + int('1'))) + " is the result"
'6 is the result'

There is almost never a need to make an expression as complicated as the one above.

Here is the same result composed with variables. This is easier to do using the editor than the interpreter. It uses more lines of code, but achieves the same result.

equivalent.py
import math
a = math.pi/2
b = math.ceil(a)
c = int('1')
d = 3 + c
e = math.floor(d)
f = b + e
g = str(f)
h = g + " is the result"
print(h)
Tracing With The Visualizer

For important examples, you can trace through (and edit) the code directly within these course notes.

Note that the program written in the editor requires a print statement to have output:

  • Python programs (written in the editor) only print to the console when specifically instructed to
  • Python expressions evaluated in the interpreter will display the result of the expression

Defining Functions

Beyond built-in functions and functions from modules, Python lets us define our own functions. This allows us to write code once and execute it many times.

simple_function.py
def hello_world():
    print("Hello, world.")

The function definition introduces new syntax:

  • The keyword def (“define”)
  • The function name with a colon (:) afterwards
  • An indented code block

You will see many more examples of this kind of syntax, with a controlling statement, a colon, and an indented block. The indentation groups a block of code with the controlling statement. It says that everything indented belongs to, and is controlled by, the function definition (in this case).


If we run the simple_function.py program above (try it), there will be no output. We have defined the function, but we have not called it.

Calling a user-defined function is the same as calling a built-in function: use the function name, followed by parentheses.

simple_function_call.py
def hello_world():
    print("Hello, world.")

hello_world()
Hello, world.

Note that the function call occurs outside of the indented block of the function definition, and remember that while the function definition does not do anything by itself, you must define a function before using it (if it isn’t a built-in function like str() or print()).

Return values and None

We haven’t specified a return value for the function, so it returns nothing. In Python, “nothing” is represented by a special variable called None. It isn’t zero, it isn’t the string 'None', it’s nothing.

Trace the execution of this program:

simple_function_call.py
def hello_world():
    print("Hello, world.")

i = int(40.1)
print(i)
j = hello_world()
print(j)

Three things are printed because there are three print statements:

  • The function definition is read
  • The expression to the right of the = on line 4 is evaluated
    • int(40.1) returns int 40
    • The result is assigned to i
  • First print: i is printed
  • The expression to the right of the = on line 6 is evaluated
    • This is a function call to hello_world()
    • Second print: 'Hello, world.' is printed by the function call
    • The function returns nothing: None
    • The expression result, None, is assigned to j
    • Third print: j is printed
Fundamentals

Trace through this program, modify it, understand it. Ask questions if you have them.

It’s a short program, but it demonstrates several concepts that are fundamental to what we will learn in the future.

print() is a built-in function that returns None in addition to creating output at the console.

To demonstrate, let’s assign the return from print to a variable:

a = print("Something")
Something

The print statement still creates the output we expected. If we print what was returned:

print(a)
None

Parameters and Arguments

We have called built-in functions with arguments, such as int(40.1). We can also define functions that take arguments:

simple_function_call.py
def rounder(v):
    str_v = str(v)
    rounded_v = int(v)
    rounded_str = str(rounded_v)
    print(str_v + " rounds to " + rounded_str)
rounder(5.2)
rounder(4.6)

Trace through the execution. Note how variable v is internal to the rounder function. Each time the function is called, \(v\) takes a different value.

Defining your own functions, especially functions that take arguments, is a powerful part of code reuse. Code reuse makes programming extremely powerful: you can write instructions once, and execute them many times, running them with different inputs to get different outputs.

Practice

Practice Problem 3.1

Practice Problem 3.1

Rewrite the program below:

  • Combine the expressions on lines 1-3 into a single expression on one line
    • The expression should yield the same result and assign to variable z
  • Keep the print(z) to check the output
practice1_3_1.py
x = 3.5
y = int(x) + 1
z = str(y) + "2"
print(z)

Practice Problem 3.2

Practice Problem 3.2

Rewrite the expression below with several intermediate variables. Keep the print statement to check your work.

practice1_3_2.py
result = float(str(int("3") + 2.5))/2 + 1
print(result)

Practice Problem 3.3

Practice Problem 3.3

The program below is meant to add up the integers between 1 and 5, take the square root of the result:

\(x = 1 + 2 + 3 + 4 + 5\)

\(y = \sqrt(x)\)

…and then print the square root. The program has an error. Fix it!

practice1_3_3.py
x = 1 + 2 + 3 + 4 + 5
y = math.sqrt(x)
print(y)

Practice Problem 3.4

Practice Problem 3.4

Write a function fixed_sequence that takes no arguments and prints the following when called:

1 2 3 4

Begin with this “starter code.”

practice1_3_4.py
def fixed_sequence(): # function definition
    # write the body of the function here

fixed_sequence() # keep these two function calls
fixed_sequence()

Keep the two function calls: when you have written the body correctly, the program should print:

1 2 3 4
1 2 3 4

Practice Problem 3.5

Practice Problem 3.5

Write a function variable_sequence that takes one argument, an integer, and prints out that integer and the next three consecutive integers in sequence:

  • variable_sequence(1) results in printed 1 2 3 4
  • variable_sequence(2) results in printed 2 3 4 5
  • variable_sequence(4) results in printed 4 5 6 7

Begin with this “starter code.”

practice1_3_5.py
def variable_sequence(first): # function definition
    # write the body of the function here

variable_sequence(1) # keep these three function calls
variable_sequence(2)
variable_sequence(4)

Keep the function calls: when you have written the body correctly, the program should print:

1 2 3 4
2 3 4 5
4 5 6 7

Homework

  • Homework problems should always be your individual work. Please review the collaboration policy and ask the course staff if you have questions.

  • Double check your file names and printed output. These need to be exact matches for you to get credit.

Homework Problem 3.1

Homework Problem 3.1 (25 pts)

Write a function squared_root that takes one argument, an integer, and prints out that integer, its square, and its square root, in sequence, with a single space in between each:

  • squared_root(1) results in printed 1 1 1
  • squared_root(4) results in printed 4 16 2
  • squared_root(0) results in printed 0 0 0

You can expect the argument will always be positive integer that is a perfect square.

Begin with this “starter code.”

squared_root.py
# any initial comments go here
def squared_root(input_int):
    # write the body of the function here

squared_root(1)
squared_root(4)
squared_root(0)

Once the function is working, remove the function calls, and keep only the function definition. Submit as squared_root.py.

Hint: You can use the str() function to convert any numeric value into a string, and the + operator between strings to concatenate (join) them.

Homework Problem 3.2

Homework Problem 3.2 (25 pts)

The program below is intended to implement the reverse_sequence function:

  • The function should take one argument, an integer
  • The function should print out a sequence of four integers:
    • The sequence starts with the argument
    • Each successive number is two less than the previous number
  • There is a single space in between each number
  • reverse_sequence(6) should print 6 4 2 0
  • reverse_sequence(3) should print 3 1 -1 -3
reverse_sqeuence.py
# initial comments
def reverse_sequence(initial):
    second = initial - 2
    third = second - 2
    fourth = third - 4
    print(initial + second + third + fourth) 

Fix/complete the function so that it works as intended. Submit as reverse_sequence.py.

Homework Problem 3.3

Homework Problem 3.3 (25 pts)

This program will contain two functions.

Write one function, hello_world that simply prints Hello, world!.

Write a second function, call_hello_world that contains a function call to the first hello_world function.

call_hello.py
# initial comments
def hello_world():
    # complete the function

def call_hello_world():
    # complete the function

call_hello_world() # this line tests your code, delete before submitting

When finished:

  • The function call at the end of the program should result in printed output Hello, world!
  • Changing the printing in the first function (hello_world) should change the output of the call to the second function
    • This is because the second function calls the first function

Before submitting, delete the call to call_hello_world. Keep the definitions. Submit as call_hello.py.

Homework Problem 3.4

Homework Problem 3.4 (25 pts)

Write a function time_convert that converts from seconds to days. It should truncate at five digits after the decimal. Output should print in the following format:

  • time_convert(500) prints 500 seconds is 0.00578 days
  • time_convert(2000) prints 2000 seconds is 0.02314 days

Submit as time_convert.py

There are 60 seconds in one minute. There are 60 minutes in one hour. There are 24 hours in one day.

You can truncate by:

  1. Multiplying by a factor of ten
  2. Calling math.floor on the result
  3. Dividing that result by the same factor of ten

The factor of ten you choose (10, 100, 1000, etc.) will determine the decimal place of the truncation.