Functions
Functions are pieces of code which perform specific tasks.We have already met some functions:
- The built-in functions, made available when you start Python
max()
,len()
,range()
, etc.
- Functions in modules, which have to be imported
math.sqrt()
,random.uniform()
, etc.
- Improve the clarity of your code
- Avoid writing the same thing twice
- This is always a bad idea (time consuming, harder to maintain your code)
- Program checks the date each day
- If
date == mother_birthday
then sends an email- Happy birthday Mum
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- Message: Happy birthday
name
- To:
name
@gmail.com
name
can be any stringThe 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)
Enhancing Clarity with Functions
Let's look at some examples how functions improve clarityExample 1
Recall that we previously studied the following problem- Compute an approximation to pi using Monte Carlo
- Note that if U is uniform on the unit square, then the probability U is in a subset B is equal to the area of B
- And that if U_1,...,U_n are IID copies of U, then the fraction in B converges to the probability of B as n gets large
- Finally, recall that for a circle, area = pi * radius^2
## 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
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
We define a function
in_circle()
- Takes as arguments a two numbers x, y
- Returns
True
if the pair is in the cirle andFalse
otherwise
- If it returns
True
, incrementcount
Example 2
Previously we considered the following problem- Write a program which prints one random outcome of following game
- 10 flips of an unbiased coin
- If 3 consecutive heads occur, pays one dollar
- If not, pays nothing
## 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)
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)
- Program is broken down into conceptual parts
sample()
has no arguments- We call it using
sample()
, notsample
Defining and Calling Functions
The syntax used to define a function isdef <name>(<parameters>): # `def` is a Python keyword used to declare functions
<body>
<parameters>
is a list of zero or more names/identifiersLet's look at an example that replicates the
count()
method for stringsdef f(string, letter):
count = 0
for s in string:
if s == letter:
count += 1
return count
f(string, letter)
is the same as string.count(letter)
Suppose this is written in a script and we run it
- The function is written in memory and stored as an object
- The identifier
f
is bound to this object
The function object is created and
f
is bound to it>>> f
<function f at 0x86e5f0c>
>>> type(f)
<type 'function'>
f
>>> f('godzilla', 'g') # Calling f()
1
>>> y = f('foo', 'o')
>>> y
2
Flow
Here's our function definition againdef f(string, letter):
count = 0
for s in string:
if s == letter:
count += 1
return count
>>> y = f('foo', 'o')
- The interpreter creates a "stack frame" to hold variable names
- The identifier
string
is registered in stack frame, bound to 'foo' - The identifier
letter
is registered in stack frame, bound to 'o' - Other "local" variables (
count
,s
) are stored in the stack frame too - Execution of the function continues until
return count
- The identifier
y
is bound to the value ofcount
- The identifier
- The stack frame and its contents are discarded
- Values of
string
,letter
,count
ands
are lost
- Values of
return
def f():
print 'This line is printed'
return 1
print 'But this line is not'
return
statementdef f():
print 'foo bar'
- execution stops at the end of the code block
- the function returns a special value called
None
return
statements- Execution terminates when the first one is met
def f(x):
if x < 0:
return 'negative'
else:
return 'nonnegative'
Arguments to functions
Any objects can be passed to a function as argumentsWe 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
def function_one(function_two):
for i in range(5):
print function_two(i)
def g(n):
return 'foo' * n
john@godzilla:~$ python -i test.py
>>> function_one(g)
foo
foofoo
foofoofoo
foofoofoofoo
Return Values
Functions always return one objectReturning two or more objects is not possible
For example, this doesn't return two values (why?)
def f():
return 1
return 2
def f():
return 1, 2
Doc Strings
The syntax for defining functions was given above asdef <name>(<parameters>):
<body>
def <name>(<parameters>):
"<doc string>"
<body>
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 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
>>> 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).
Exercises
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
- A function
f
which maps a float into a float - An interval
[a, b]
(i.e., two floatsa
andb
) - An integer
n
- A number
x
- the piecewise linear interpolation value at x,
- based on n evenly spaced grid points
a = x[0] < x[1] < ... < x[n-1] = b
n = 3
Solutions
Solution to Problem 1:def f(string):
count = 0
for letter in string:
if letter == letter.upper():
count += 1
return count
def f(A, B):
subset = True
for a in A:
if a not in B:
subset = False
return subset
## 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)
0 comments:
Post a Comment