Functions in Python
- 1 What is a function?
- 2 Defining a function
- 3 Null function
- 4 Functions are objects
- 5 Functions can be nested inside a function
- 6 Closures
- 7 Attributes
- 8 Decorators
- 9 Revisit the persistent variable
- 10 Have you noticed this?
- 11 Exercise: Define your first function
- 12 Exercise: Non-returning function
- 13 Exercise: Add function
- 14 Exercise: Why functions?
- 15 Exercise: Distance function
- 16 Exercise: Yet another distance function
- 17 Exercise: File-writing function
- 18 Exercise: File-reading function
- 19 Exercise: Directory-listing function
- 20 Exercise: Create number files
- 21 Exercise: Add numbers from the number files
- 22 Exercise: Generate random integers
- 23 Exercise: User-input function
- 24 Exercise: Plot a GeoTIFF file
- 25 Exercise: Calculate the average cell value of a GeoTIFF file
- 26 Homework: Say rock paper scissors
- 27 Homework: Read the user’s rock paper scissors
- 28 Homework: Rock paper scissors game
- 29 Homework: Advanced rock paper scissors game
- 30 Homework: Arithmetic calculator
1 What is a function?
A function is a set of statements.
It can take arguments and return a value.
We mainly use functions to reduce redundancy in code.
We also use functions for better code readability.
Sometimes, we need them for recursive programming.
2 Defining a function
Define a simple function.
def f(x):
y = 2*x
return y
print(f(3))
Define it again.
def f(x): y = 2*x; return y
print(f(3))
3 Null function
Can you define a function with no body? Let’s try!
def nop():
nop()
Not working! Use the pass
statement as a placeholder.
def nop(): pass # just do nothing
nop()
4 Functions are objects
A function is an instance of a class (object) with its name.
def foo():
""" function document
called doc string """
return 'foo';
print(dir()) # lists all names in the current scope
print(dir(foo)) # lists all valid attributed for the foo object
Can you see the __class__
attribute? Try this.
def foo():
""" function document
called doc string """
return 'foo';
print(dir())
print(dir(foo))
print(foo.__name__)
print(foo.__doc__)
print(foo.__call__())
5 Functions can be nested inside a function
We can limit the scope of a function to its parent function.
def some_math(x, y):
def add(x, y):
z = x + y
print('{x}+{y}={z}'.format(x=x, y=y, z=z))
def mul(x, y):
z = x * y
print(f'{x}*{y}={z}') # shorter way of formatting strings in Python 3.6
add(x, y)
mul(x, y)
some_math(10, 2)
6 Closures
Normally, any local variables defined inside a function do not persist.
def accum(x):
total += x # well, there is even no way to initialize total
return total
print(accum(1))
print(accum(2))
Use a nested function to make local variables persistent.
def accum_func():
total = 0 # initialize it
def add(x):
nonlocal total # do you remember nonlocal? total is the total above
total += x
return total
return add # return the add function itself
accum = accum_func()
print(accum(1))
print(accum(2))
The above programming pattern is called closure.
7 Attributes
We can define function attributes.
def accum(x):
accum.total += x
return accum.total
accum.total = 0 # create a new attribute
print(accum(1))
print(accum(2))
print(accum.total)
8 Decorators
Add pre-/post-statements that wrap a function.
def decorator(func):
def wrapper():
print("before func")
func()
print("after func")
return wrapper
def f():
print("f")
f = decorator(f)
f()
# the "pie" syntax
# equivalent to g = decorator(g)
@decorator
def g():
print("g")
g()
9 Revisit the persistent variable
We can use the decorator pattern to declare function attributes.
def static_decorator(name, val):
def wrapper(func):
setattr(func, name, val)
return func
return wrapper
# equivalent to accum = static_decorator('total', 0)(accum)
@static_decorator('total', 0)
def accum(x):
accum.total += x
return accum.total
print(accum(1))
print(accum(2))
10 Have you noticed this?
Functions can be variables.
def f(g): # f takes a function and just returns it
return g
def h(x): # h takes an argument and prints it
print(x)
a = f(h) # a == h
a(12) # calls h(12)
In C, we call them function pointers.
11 Exercise: Define your first function
Functions are a group of statements and expressions. They may return a value optionally using a return
statement. Function definitions start with def
, their name, and optional argument names with parentheses, followed by a colon. They take a logical block, meaning you have to indent the entire function body so it can belong to its function definition. Guess what this function does:
# define your function
def f():
return "Hello"
# call your function and assign its return value to x
x = f()
# print x
print(x)
12 Exercise: Non-returning function
How is this function different from your first function that returned "Hello"
?
# define your function
def f():
print("Hello")
# call your function; this time f() does not return anything,
# so we don't have anything to assign to another variable
f()
13 Exercise: Add function
Let’s define a more useful function. What about an addition function?
def add(x, y):
return x + y
z = add(10, 5)
print(z)
14 Exercise: Why functions?
Well, just adding two numbers is very trivial and you wouldn’t need to define a function for that. Then, why do we need functions? First, you can separate meaningful logic as a separate body. Second, when you have to repeat the same logic. Let’s see. Calculate the factorial of 5 (5!) and 10!.
##### without a function
fact = 1
for i in range(1, 5+1): # meaning from 1 to 5, NOT including 0 because multiplying fact by 0
# would give you 0.
fact = fact * i
print(fact)
fact = 1
for i in range(1, 10+1):
fact = fact * i
print(fact)
# what do you think? Both for loops are almost the same except 5 and 10, very repetitive
##### with a function
def factorial(n):
fact = 1
for i in range(1, n+1): # from 1 to n
fact = fact * i
return fact
print(factorial(5))
print(factorial(10))
# would you use the non-function version or the function version for your repeating task?
# also, we made it very clear that the for loop is for calculating a factorial by separating out
# the loop in a function
15 Exercise: Distance function
Let’s try geospatial computation. Oh! Function names can only contain alphabets, numbers, and underscores (_), and they cannot start with a number.
def calc_distance(x1, y1, x2, y2): # take two point coordinates
return ((x1 - x2)**2 + (y1 - y2)**2)**0.5 # power to 0.5 means square root
point1 = (10, 20) # use a tuple because we'll never change the coordinates of point1
point2 = (100, 200)
x1 = point1[0]
y1 = point1[1]
x2 = point2[0]
y2 = point2[1]
dist = calc_distance(x1, y1, x2, y2)
print(dist)
16 Exercise: Yet another distance function
You didn’t like the x1 = point1[0], y1 = point1[1], ...
part of the first distance function because you’ll have to repeat it when you need to calculate multiple distances? You can move that block to the function. Let’s try this.
def calc_distance(point1, point2):
x1 = point1[0]
y1 = point1[1]
x2 = point2[0]
y2 = point2[1]
return ((x1 - x2)**2 + (y1 - y2)**2)**0.5
p1 = (10, 20)
p2 = (100, 200)
dist = calc_distance(p1, p2) # pass two point tuples
print(dist)
print(calc_distance((10, 20), (100, 200))) # equivalent
17 Exercise: File-writing function
Files can be created using the open()
, write()
, and close()
functions. The open()
function creates a file object who has the write()
and close()
functions. Let’s try.
def write_file(filename, content):
f = open(filename, "w") # "w" means that open this file for writing; it'll overwrite the file
# if any; just calling f = open(filename) would attempt to read
# an existing file
f.write(content) # write out your content to the file; here we use f that is returned from open();
# that's the connection between your content and the physical file
f.close() # close the file; this is important!
# remember backslashes have special meanings, so you need to either repeat them twice ("c:\\test.txt")
# or use an r-string (r"c:\test.txt")
write_file(r"c:\test.txt", "Testing write_file()")
# let's see if open(..., "w") really overwrite an existing file
write_file("c:\\test.txt", "Testing write_file() second time")
# you can use forward slashes
write_file("c:/test.txt", "Testing write_file() third time")
# check c:\test.txt and what's in there?
18 Exercise: File-reading function
Can we read files? Of course! Let’s try this.
def read_file(filename):
f = open(filename) # do you remember this function without "w"? yes, that's opening a file for reading
content = f.read() # read the entire file content into content
f.close() # again, it's important to close the file
return content
print(read_file("c:/test.txt")) # what does it print?
19 Exercise: Directory-listing function
Let’s try to list all subdirectories and files in a directory. We’ll use globbing for listing all items in a directory.
import glob # need to import the glob module for globbing
def list_dir(dir):
return glob.glob(dir + "/*") # find all (*) items in dir and return them as a list
for i in list_dir("c:/")
print(i)
20 Exercise: Create number files
Using the write_file()
function, let’s create 10 files with numbers.
def write_file(filename, content):
f = open(filename, "w")
f.write(content)
f.close()
for i in range(1, 11): # from 1 to 10
filename = "c:/num" + str(i) + ".txt" # do you remember type casting?
write_file(filename, str(i)) # yes, the write() function only accepts strings, so you need type casting
# check your C:\; can you see num1.txt, ..., num10.txt
21 Exercise: Add numbers from the number files
Now, let’s try to add numbers from the number files using the read_file()
function.
import glob # we'll use globbing to grab all num*.txt files
def list_num_txt_files(dir):
return glob.glob(dir + "/num*.txt") # return all filenames starting with num and ending with .txt
def read_file(filename):
f = open(filename)
content = f.read()
f.close()
return content
x = 0
for filename in list_num_txt_files("c:/"):
content = read_file(filename) # content is a string
num = int(content) # type-cast content to int
x = x + num # accumulate num to x
print(x) # print x; what is your x?
22 Exercise: Generate random integers
You can generate random integers using the random module.
import random
def generate_random_int(start, end): # this function returns a random integer between start and end
return random.randint(start, end) # unlike range(), it returns an integer including start and end
for i in range(100): # print a random integer between 1 and 10, 100 times
print(generate_random_int(1, 10))
23 Exercise: User-input function
Let’s define a function that says “What did you eat yesterday? (p)izza, (b)urger, (s)teak, (o)ther”, reads, and returns a user response.
def what_did_you_eat(): # doesn't need any arguments
while True: # we go into an infinite loop until the user picks one of the right choices (p, b, s, o)
# do you remember the input() function?
you_ate = input("What did you eat yesterday? (p)izza, (b)urger, (s)teak, (o)ther ")
if you_ate == "p":
you_ate = "pizza" # return a full name
break # break out of the while loop because p is one of the choices
elif you_ate == "b":
you_ate = "burger"
break # break out of the while loop because b is one of the choices
elif you_ate == "s":
you_ate = "steak"
break # break out of the while loop because s is one of the choices
elif you_ate == "o":
you_ate = "other"
break # break out of the while loop because o is one of the choices
else:
print("Please use one of p, b, s, and o only")
return you_ate
i_ate = what_did_you_eat()
print(i_ate)
24 Exercise: Plot a GeoTIFF file
Download elevation.tif and try the following code. Make sure to change the path to elevation.tif to yours.
# we'll use the gdal module
from osgeo import gdal
# we'll use matplotlib.pyplot to plot raster arrays; here, "as plt" creates
# a shortcut plt for its full name matplotlib.pyplot
import matplotlib.pyplot as plt
def read_geotiff(filename):
# read in filename as a GDAL dataset object
ds = gdal.Open(filename)
# get the first and only band; note here that band numbering is 1-based
band = ds.GetRasterBand(1)
# read the band as a matrix
arr = band.ReadAsArray()
# now, release ds and band
del ds, band
return arr
# read elevation.tif into elev; of course, use your path to elevation.tif
elev_arr = read_geotiff("elevation.tif")
# plot elev in memory using the "im"age "show" function
plt.imshow(elev_arr)
# show the in-memory plot
plt.show()
25 Exercise: Calculate the average cell value of a GeoTIFF file
Download elevation.tif and try the following code. Make sure to change the path to elevation.tif to yours. We use the numpy.ndarray.sum method to calculate the sum of raster values.
from osgeo import gdal
def read_geotiff(filename):
ds = gdal.Open(filename)
band = ds.GetRasterBand(1)
arr = band.ReadAsArray()
del ds, band
return arr
def calc_average(arr):
# number of rows
num_rows = arr.shape[0]
# number of columns
num_cols = arr.shape[1]
# number of cells
num_cells = num_rows * num_cols
# sum of array cell values; sum() is a method in a NumPy array
sum_arr = arr.sum()
# average array cell value
avg_arr = sum_arr / num_cells
return avg_arr
elev_arr = read_geotiff("elevation.tif")
# calculate the average elevation and type-cast it to int
avg_elev = int(calc_average(elev_arr))
print("The average elevation is", avg_elev)
26 Homework: Say rock paper scissors
Define a function called i_rock()
that generates a random integer between 1 and 3, and returns rock
if the generated number is 1, paper
if 2, and scissors
if 3. This function doesn’t need to take any arguments and it returns one of three strings rock
, paper
, and scissors
based on a random integer it generates. Just submit your function definition only in FirstLastname_i_rock.py
.
27 Homework: Read the user’s rock paper scissors
Define a function called you_rock()
that prints “(r)ock, (p)aper, (s)cissors, (q)uit?
”, reads a string from the user using the input()
function, and returns rock
if the input is r
, paper
if p
, scissors
if s
, and quit
otherwise. Just submit your function definition only in FirstLastname_you_rock.py
.
28 Homework: Rock paper scissors game
Use the random.randint()
, print()
, input()
, and your own functions to implement the rock paper scissors game.
- Define a function called
i_rock()
that generates a random integer between 1 and 3 usingrandom.randint()
and returnsrock
if the random number is 1,paper
if 2, andscissors
if 3 - Define another function called
you_rock()
that prints “(r)ock, (p)aper, (s)cissors, (q)uit?
”, reads an input string using theinput()
function, and returnsrock
if the input isr
,paper
ifp
,scissors
ifs
, andquit
otherwise - Write an infinite
while
loop that does the following:- Call
i_rock()
and store its return value inmy_throw
- Call
you_rock()
and store its return value inyour_throw
- Break out of the
while
loop ifyour_throw
isquit
- Now,
your_throw
is notquit
because you didn’t break thewhile
loop in the above step - Compare
my_throw
andyour_throw
, and print “You won
”, “You lost
”, or “We tied
” usingif-elif-else
branching
- Call
Submit your script in FirstLastname_rock_game.py
.
29 Homework: Advanced rock paper scissors game
Use the random.random()
, print()
, input()
, and your own functions to implement the rock paper scissors game. There is one little problem. Your code has a bad habit of throwing a rock twice more likely than the other two.
- Generate a random number between 0 and 1 using
random.random()
- Determine the computer’s rock (coded as
1
), paper (2
), or scissors (3
) based on the random number, but how can you code your code’s bad habit? Think about that - Pass the computer’s throw from step 2 to the
play_once()
function that does the following:- Print “
(r)ock, (p)aper, (s)cissors, (q)uit?
” and read a string input - If the input is
q
, return0
- Return
1
if the user won - Return
2
if the user lost - Return
3
if both players tie
- Print “
- Get the return value of
play_once()
- If the return value is
0
, printWins: **%, Losses: **%, Ties: **%
and stop - Print
You won
,You lost
, orYou tied
and count wins, losses, and ties - Go to step 1
Submit your script in FirstLastname_advanced_rock_game.py
.
30 Homework: Arithmetic calculator
Write a simple calculator that supports four basic arithmetic operations (+
, -
, *
, and /
) for two floating-point numbers.
- Print “
?
” and read an expression - Pass the expression to your function called
evaluate()
- If the expression is
quit
, returnNone
- Split the expression into three parts: left operand, operator, and right operand
- Branch based on the operator
- Calculate the expression and return its result
- If the expression is
- Take the return value of
evaluate()
- If the return value is
None
, stop - Print the result
- Go to step 1
Submit your script in FirstLastname_calc.py
.