Functions and abstraction
Andrew S. Fitz Gibbon
UW CSE 160
Winter 2024
1
Functions
In math:
Python:
2
Python Functions
In Python:
3
Similar to what we saw with loops
Using ("calling") a function
len("hello") len("")
math.sqrt(9) math.sqrt(7)
range(1, 5) range(8)
math.sin(0) str(17)
4
Function call examples
import math
x = 8
y = 16
z = math.sqrt(16)
u = math.sqrt(y)
v = math.sqrt(8 + 8)
w = math.sqrt(x + x)
greeting = "hi"
name = "Fitz"
a = len("hello")
b = len(greeting)
c = len("hello" + "Fitz")
d = len(greeting + name)
print("hello")
print()
print(len(greeting + name))
What are the:
5
Some functions are like a machine
6
2x + 1
x
2x + 1
x
2x + 1
x
100
0
2
5
201
1
In math: func(x) = 2x + 1
Defining a function
Define the machine,�including the input and the result
7
def dbl_plus(x):
return 2 * x + 1
Keyword that means:�I am defining a function
Keyword that means:�This is the result
Input variable name,�or “formal parameter”
Name of the function.�Like “y = 5” for a variable
2x + 1
x
Return expression�(part of the return statement)
How Python executes a function call
8
Formal parameter (a variable)
def square(x):
return x * x
square(3 + 4)
Function definition
Function call or�function invocation, �the “call site”
Example function call:
y = 1 + square(3 + 4)
y = 1 + square(7)
Variables:
x: 7
return x * x
return 7 * x
return 7 * 7
return 49
Argument or �“actual parameter”
y = 1 + 49
y = 50
Practice Reading Functions
x = 1
z = 3.1
def multiply(x, y):
# (1) What are the values of x and y here
# the first time multiply is called?
print("x,y:", x, y)
z = 0
return x * y
x = 2
y = 1
result = multiply(3, y)
print(y) # (2) What is the value of y here?
result = multiply(x, 4)
print(result) # (3) What is the value of result here?
print(z) # (4) What is the value of z here?
9
Function definition examples
def dbl_plus(x):
return 2 * x + 1
def instructor_name():
return "Andrew Fitz Gibbon"
def square(x):
return x * x
def calc_grade(points):
grade = points * 10
return grade
10
For each function definition, identify:
Function definitions and calls
def dbl_plus(x):
return 2 * x + 1
def instructor_name():
return "Andrew Fitz Gibbon"
def calc_grade(points):
grade = points * 10
return grade
# main program
dbp3 = dbl_plus(3)
dbp4 = dbl_plus(4)
print(dbp3 + dbp4)
print(instructor_name())
my_grade = calc_grade(dbp3)
11
Identify:
This is all in the same file
More function definitions and calls
def square(x):
return x * x
def print_greeting():
print("Hello, world")
def calc_grade(points):
grade = points * 10
return grade
def print_grade(points):
grade = points * 10
print("Grade is:", grade)
# main program
sq1 = square(3)
print_greeting()
my_grade = calc_grade(sq1)
print_grade(5)
12
No return statement
Returns the value None
Executed for side effect
No return statement
Returns the value None
Executed for side effect
This is all in the same file
How many x variables?
def square(x):
return x * x
def abs(x):
if x < 0:
return –x
else:
return x
# main program
x = 42�sq3 = square(3)�sq4 = square(4)�print(sq3 + sq4)�print(x)�x = -22�result = abs(x)�print(result)
13
This is all in the same file
Functions can call functions
def fahr_to_cent(fahr):
return (fahr – 32) / 9.0 * 5
def cent_to_fahr(cent):
result = cent / 5.0 * 9 + 32
return result
def print_fahr_to_cent(fahr):
result = fahr_to_cent(fahr)
print(result)
# main program
boiling = fahr_to_cent(212)
cold = cent_to_fahr(-30)
print(print_fahr_to_cent(32))
14
This is all in the same file
No return statement
Returns the value None
Executed for side effect
Two types of output
15
In a function body, assignment creates a temporary variable (like the formal parameter)
def store_it(arg):
stored = arg
return stored
stored = 0
y = store_it(22)
print(y)
print(stored)
16
How to look up a variable
Idea: find the nearest variable of the given name
If a local and a global variable have the same name, the global variable is inaccessible (“shadowed” or “masked”)
This is confusing; try to avoid shadowing
x = 22
stored = 100
def lookup():
x = 42
return stored + x
val = lookup()
x = 5
stored = 200
val = lookup()
17
def lookup():
x = 42
return stored + x
x = 22
stored = 100
val = lookup()
x = 5
stored = 200
val = lookup()
What happens if we define stored after lookup?
Local variables exist �only while the function is executing
def cent_to_fahr(cent):
result = cent / 5.0 * 9 + 32
return result
tempf = cent_to_fahr(15)
print(result)
18
Use only the local and the global scope!
myvar = 1
def outer():
myvar = 1000
temp = inner()
return temp
def inner():
return myvar
print(outer())
19
Functions are an Abstraction
20
Defining absolute value
def abs(x):
if x < 0:
return -1 * x
else:
return 1 * x
def abs(x):
if x < 0:
return -x
else:
return x
def abs(x):
if x < 0:
result = -x
else:
result = x
return result
def abs(x):
return math.sqrt(x * x)
21
Defining round�(for positive numbers)
def round(x):
return int(x + 0.5)
def round(x):
fraction = x - int(x)
if fraction >= 0.5:
return int(x) + 1
else:
return int(x)
22
Two types of documentation
23
Two types of documentation
24
For programmers: arbitrary text after #
For users: a string as the first element of the function body
def square(x):
"""Returns the square of its argument."""
# Uses "x*x" instead of "x**2"
return x * x
Multi-line strings
25
Don’t write useless comments
# increment the value of x
x = x + 1
26
DO NOT write comments like this.
Where to write comments
# We count down to 0 here because our users expect
# results to be in descending order.
for i in range(5, -1, -1):
...
x = y + x # a comment about this line
27
Decomposing a problem
28
How to design a function
1. Wishful thinking: Write the program as if the function already exists
2. Write a specification: Describe the inputs and output, including their types
No implementation yet!
3. Write tests: Example inputs and outputs
4. Write the function body (the implementation)
First, write your plan in English, then translate to Python
def fahr_to_cent(fahr):
"""
Input: a number representing degrees Fahrenheit
Return value: a number representing degrees centigrade
"""
result = (fahr – 32) / 9.0 * 5
return result
assert fahr_to_cent(32) == 0
assert fahr_to_cent(212) == 100
assert fahr_to_cent(98.6) == 37
assert fahr_to_cent(-40) == -40
# Main program
tempf = 32
print("Temperature in Fahrenheit:", tempf)
tempc = fahr_to_cent(tempf)
print("Temperature in Celsius:", tempc)
29
More Examples
def cent_to_fahr(cent):
print(cent / 5.0 * 9 + 32)
print(cent_to_fahr(20))
def c_to_f(c):
print "c_to_f"
return c / 5.0 * 9 + 32
def make_message(temp):
print("make_message")
return "The temperature is " + str(temp)
for tempc in [-40, 0, 37]:
tempf = c_to_f(tempc)
message = make_message(tempf)
print(message)
30
def myfunc(n):
total = 0
for i in range(n):
total = total + i
return total
print(myfunc(4))
double(7)
Use the Python Tutor: http://pythontutor.com/
abs(-20 - 2) + 20
What does this print?
def cent_to_fahr(cent):
print(cent / 5.0 * 9 + 32)
print(cent_to_fahr(20))
31
What does this print?
def myfunc(n):
total = 0
for i in range(n):
total = total + i
return total
print(myfunc(4))
32
What does this print?
def c_to_f(c):
print("c_to_f")
return c / 5.0 * 9 + 32
def make_message(temp):
print("make_message")
return "The temperature is " + str(temp)
for tempc in [-40, 0, 37]:
tempf = c_to_f(tempc)
message = make_message(tempf)
print(message)
33
What does this print?
def c_to_f(c):
print("c_to_f”)
return c / 5.0 * 9 + 32
def make_message(temp):
print("make_message”)
return "The temperature is " + str(temp)
for tempc in [-40, 0, 37]:
tempf = c_to_f(tempc)
message = make_message(tempf)
print(message)
c_to_f
make_message
The temperature is -40.0
c_to_f
make_message
The temperature is 32.0
c_to_f
make_message
The temperature is 98.6
34
Each variable should represent one thing
def atm_to_mbar(pressure):
return pressure * 1013.25
def mbar_to_mmHg(pressure):
return pressure * 0.75006
# Confusing
pressure = 1.2 # in atmospheres
pressure = atm_to_mbar(pressure)
pressure = mbar_to_mmHg(pressure)
print(pressure)
# Better
in_atm = 1.2
in_mbar = atm_to_mbar(in_atm)
in_mmHg = mbar_to_mmHg(in_mbar)
print(in_mmHg)
# Best
def atm_to_mmHg(pressure):
in_mbar = atm_to_mbar(pressure)
in_mmHg = mbar_to_mmHg(in_mbar)
return in_mmHg
print(atm_to_mmHg(1.2))
Corollary: Each variable should contain values of only one type
# Legal, but confusing: don’t do this!
x = 3
…
x = "hello"
…
x = [3, 1, 4, 1, 5]
…
35
Review: how to evaluate a function call
36
HW2 Questions
37
Bonus Slides: �Extra Function Calls
38
Example of function invocation
def square(x):
return x * x
Variables:
square(3) + square(4) (none)
return x * x x: 3
return 3 * x x: 3
return 3 * 3 x: 3
return 9 x: 3
9 + square(4) (none)
return x * x x: 4
return 4 * x x: 4
return 4 * 4 x: 4
return 16 x: 4
9 + 16 (none)
25 (none)
39
Expression with nested function invocations:�Only one executes at a time
def fahr_to_cent(fahr):
return (fahr – 32) / 9.0 * 5
def cent_to_fahr(cent):
return cent / 5.0 * 9 + 32
Variables:
fahr_to_cent(cent_to_fahr(20)) (none)
return cent / 5.0 * 9 + 32 cent: 20
return 20 / 5.0 * 9 + 32 cent: 20
return 68 cent: 20
fahr_to_cent(68) (none)
return (fahr – 32) / 9.0 * 5 fahr: 68
return (68 – 32) / 9.0 * 5 fahr: 68
return 20 fahr: 68
20 (none)
40
Expression with nested function invocations:�Only one executes at a time
def square(x):
return x * x
Variables:
square(square(3)) (none)
return x * x x: 3
return 3 * x x: 3
return 3 * 3 x: 3
return 9 x: 3
square(9) (none)
return x * x x: 9
return 9 * x x: 9
return 9 * 9 x: 9
return 81 x: 9
81 (none)
41
Function that invokes another function:�Both function invocations are active
def square(z):
return z * z
def hypotenuse(x, y):
return math.sqrt(square(x) + square(y))
Variables:
hypotenuse(3, 4) (none)
return math.sqrt(square(x) + square(y)) x: 3 y:4
return math.sqrt(square(3) + square(y)) x: 3 y:4
return z * z z: 3 x: 3 y:4
return 3 * 3 z: 3 x: 3 y:4
return 9 z: 3 x: 3 y:4
return math.sqrt(9 + square(y)) x: 3 y:4
return math.sqrt(9 + square(4)) x: 3 y:4
return z * z z: 4 x: 3 y:4
return 4 * 4 z: 4 x: 3 y:4
return 16 z: 4 x: 3 y:4
return math.sqrt(9 + 16) x: 3 y:4
return math.sqrt(25) x: 3 y:4
return 5 x: 3 y:4
5 (none)
42
Shadowing of formal variable names
def square(x):
return x * x
def hypotenuse(x, y):
return math.sqrt(square(x) + square(y))
Variables:
hypotenuse(3, 4) (none)
return math.sqrt(square(x) + square(y)) x: 3 y:4
return math.sqrt(square(3) + square(y)) x: 3 y:4
return x * x x: 3 x: 3 y:4
return 3 * 3 x: 3 x: 3 y:4
return 9 x: 3 x: 3 y:4
return math.sqrt(9 + square(y)) x: 3 y:4
return math.sqrt(9 + square(4)) x: 3 y:4
return x * x x: 4 x: 3 y:4
return 4 * 4 x: 4 x: 3 y:4
return 16 x: 4 x: 3 y:4
return math.sqrt(9 + 16) x: 3 y:4
return math.sqrt(25) x: 3 y:4
return 5 x: 3 y:4
5 (none)
43
Same formal parameter name
Same formal parameter name, �but two completely different variables
Formal parameter is a new variable
Shadowing of formal variable names
def square(x):
return x * x
def hypotenuse(x, y):
return math.sqrt(square(x) + square(y))
Variables:
hypotenuse(3, 4) (none) hypotenuse()
return math.sqrt(square(x) + square(y)) x: 3 y:4
return math.sqrt(square(3) + square(y)) square() x: 3 y:4
return x * x x: 3 x: 3 y:4
return 3 * 3 x: 3 x: 3 y:4
return 9 x: 3 x: 3 y:4
return math.sqrt(9 + square(y)) x: 3 y:4
return math.sqrt(9 + square(4)) square() x: 3 y:4
return x * x x: 4 x: 3 y:4
return 4 * 4 x: 4 x: 3 y:4
return 16 x: 4 x: 3 y:4
return math.sqrt(9 + 16) x: 3 y:4
return math.sqrt(25) x: 3 y:4
return 5 x: 3 y:4
5 (none)
44
Same diagram, with variable scopes or environment frames shown explicitly