Functions are pieces of code which perform specific tasks.
We have already met some functions:
max(), len(), range(), etc.math.sqrt(), random.uniform(), etc.It is also useful to be able to define our own functions
Improve the clarity of your code
Avoid writing the same thing twice
For example, let's say you write a program to send an email to your mother on her birthday
date == mother_birthday then sends an emailYour mother is happy, because you never forget her birthday
Now you decide you want to send an email on your father's birthday, sister's birthday, etc.
If you write a new program each time you will repeat a lot of the same code
Better to write one function email(name) which sends one email
name name@gmail.comThe argument name can be any string
The program looks something like this (in pseudocode)
birthdays = {'mum': '02-07',
'dad': '12-03',
'sis': '07-11'}
define function email(name):
message = 'Happy birthday %s!!' % name
address = '%s@gmail.com' % name
send message to address # This pseudocode is pretty easy to code up in Python
repeat every morning:
get date
for name, birthday in birthdays.items():
if birthday == date:
email(name)
Let's look at some examples how functions improve clarity
Recall that we previously studied the following problem
The solution we used was
## Filename: circle.py
## Author: John Stachurski
# Since pi = area / radius**2, we need to estimate the area of the circle
# and then divide by radius**2 = (1/2)**2 = 1/4. First we estimate the area
# by sampling bivariate uniforms and looking at the fraction that fall into
# the circle.
from random import uniform
from math import sqrt
n = 100000
count = 0
for i in range(n):
U, V = uniform(0, 1), uniform(0, 1)
d = sqrt((U - 0.5)**2 + (V - 0.5)**2)
if d < 0.5:
count += 1
area_estimate = count / float(n)
print area_estimate * 4 # dividing by radius**2
At first glance it's not so clear what this program does
Let's try to make it more readable using a function
## Filename: circle2.py
## Author: John Stachurski
from random import uniform
from math import sqrt
# First we define a new function, called in_circle
def in_circle(x, y):
"""
Tests whether (x,y) is in the circle of radius 0.5, centered on
the point (0.5, 0.5).
"""
if sqrt((x - 0.5)**2 + (y - 0.5)**2) < 0.5:
return True
else:
return False
# Now the main loop
n = 100000
count = 0
for i in range(n):
U, V = uniform(0, 1), uniform(0, 1)
if in_circle(U, V):
count += 1
area_estimate = count / float(n)
print area_estimate * 4 # dividing by r**2
Okay, so the code is longer, but the logic is clearer (which is more important)
We define a function in_circle()
True if the pair is in the cirle and False otherwiseIn the main loop we call the function
True, increment countThe syntax for defining functions is explained below
Previously we considered the following problem
Next we wrote a program to estimate average payoff
## Filename: 3heads3.py
## Author: John Stachurski
from random import uniform
n = 100000
outcomes = []
for i in range(n):
payoff = 0
count = 0
for j in range(10):
U = uniform(0, 1)
count = count + 1 if U < 0.5 else 0
if count == 3:
payoff = 1
break
outcomes.append(payoff)
print sum(outcomes) / float(n)
The main loop is long and complicated
We can clarify the logic by generating payoffs as a function:
## Filename: 3heads4.py
## Author: John Stachurski
from random import uniform
def sample():
"""
Generates random payoff from one round of the game.
If 3 consecutive heads occur, returns 1. Else, returns 0.
"""
payoff = 0
count = 0
for j in range(10):
U = uniform(0, 1)
count = count + 1 if U < 0.5 else 0
if count == 3:
payoff = 1
break
return payoff
# Main loop
n = 100000
outcomes = [sample() for i in range(n)] # The loop
print sum(outcomes) / float(n)
Again, the program is a bit longer, but the logic is clearer
Notice that the function sample() has no arguments
sample(), not sampleThe syntax used to define a function is
def <name>(<parameters>): # `def` is a Python keyword used to declare functions
<body>
Here <parameters> is a list of zero or more names/identifiers
Let's look at an example that replicates the count() method for strings
def f(string, letter):
count = 0
for s in string:
if s == letter:
count += 1
return count
Calling f(string, letter) is the same as string.count(letter)
Suppose this is written in a script and we run it
f is bound to this objectHere we run it in IDLE
The function object is created and f is bound to it
>>> f
<function f at 0x86e5f0c>
The function object has been stored at memory location 0x86e5f0c
>>> type(f)
<type 'function'>
Now we can call (i.e., use) f
>>> f('godzilla', 'g') # Calling f()
1
>>> y = f('foo', 'o')
>>> y
2
Here's our function definition again
def f(string, letter):
count = 0
for s in string:
if s == letter:
count += 1
return count
Let's step through execution of the statement
>>> y = f('foo', 'o')
string is registered in stack frame, bound to 'foo'letter is registered in stack frame, bound to 'o'count, s) are stored in the stack frame tooreturn county is bound to the value of countstring, letter, count and s are lostTypically, execution of the function body continues until it hits return
def f():
print 'This line is printed'
return 1
print 'But this line is not'
It is possible to have no return statement
def f():
print 'foo bar'
In this case
NoneWe can have multiple return statements
def f(x):
if x < 0:
return 'negative'
else:
return 'nonnegative'
Any objects can be passed to a function as arguments
We have seen functions passed numbers and strings
Lists and tuples are okay too
Here's a function which is passed a list/tuple X and returns sum(X)
def sum2(X):
count = 0
for x in X:
count += x
return count
In fact we can pass functions as arguments just as easily!
def function_one(function_two):
for i in range(5):
print function_two(i)
def g(n):
return 'foo' * n
Here is what happens
john@godzilla:~$ python -i test.py
>>> function_one(g)
foo
foofoo
foofoofoo
foofoofoofoo
Can you see how this works?
Functions always return one object
Returning two or more objects is not possible
For example, this doesn't return two values (why?)
def f():
return 1
return 2
Here is another attempt to return two objects (integers):
def f():
return 1, 2
But this returns one object: a tuple
The syntax for defining functions was given above as
def <name>(<parameters>):
<body>
Actually there is another optional element: a doc string
def <name>(<parameters>):
"<doc string>"
<body>
Previously we saw the folloing example
def in_circle(x, y):
"""
Tests whether (x,y) is in the circle of radius
0.5, centered on (0.5, 0.5).
"""
if sqrt((x - 0.5)**2 + (y - 0.5)**2) < 0.5:
return True
else:
return False
Single quotes, double quotes, triple quotes all fine
The doc string is just a comment, so we could also use a hash
def in_circle(x, y):
# Tests whether (x,y) is in the circle of radius
# 0.5, centered on (0.5, 0.5).
if sqrt((x - 0.5)**2 + (y - 0.5)**2) < 0.5:
return True
else:
return False
The advantage of a doc string is that it is available at runtime
>>> in_circle
<function in_circle at 0x866048c>
>>> help(in_circle)
Help on function in_circle in module __main__:
in_circle(x, y)
Tests whether (x,y) is in the circle of radius
0.5, centered on (0.5, 0.5).
Problem 1:
Write a function which takes a string as an argument and returns the number of capital letters in the string
Problem 2:
Write a function which takes two sequences A and B as arguments and
returns True if every element in A is also an element of B, else
False
Problem 3:
Write a function which takes as arguments
f which maps a float into a float[a, b] (i.e., two floats a and b)nxReturns
a = x[0] < x[1] < ... < x[n-1] = bHere is an example with n = 3
Solution to Problem 1:
def f(string):
count = 0
for letter in string:
if letter == letter.upper():
count += 1
return count
Solution to Problem 2:
def f(A, B):
subset = True
for a in A:
if a not in B:
subset = False
return subset
Solution to Problem 3:
## Filename: linapprox.py
## Author: John Stachurski
from __future__ import division
def linapprox(f, a, b, n, x):
"""
Evaluates piecewise linear interpolant of f at x on the interval [a, b],
with n evenly spaced grid points.
Parameters:
f is a function
x, a and b are numbers with a <= x <= b
n is an integer.
Returns:
A number (the interpolant evaluated at x)
"""
length_of_interval = b - a
num_subintervals = n - 1
step = length_of_interval / num_subintervals # Distance between grid points
# Find the first grid point that is larger than x
point = a
while point <= x:
point += step
# Now x must lie between the gridpoints (point - step) and point
u, v = point - step, point
return f(u) + (x - u) * (f(v) - f(u)) / (v - u)