Functions
A function takes input parameters, executes a series of computations with those inputs and then returns a final output value. Functions give us an efficient way to save and reuse a block of code over and over again with different input values. In this section, we summarize the builtin functions in the standard Python library and then we discuss how to define our own functions.
Builtin Functions
The standard Python library has a collection of builtin functions ready for us to use. We have already seen a few of these functions in previous sections such as type()
, print()
and sum()
. The following is a list of builtin functions that we'll use most often:
Function  Description 

print(object) 
print object to output 
type(object) 
return the type of object 
abs(x) 
return the absolute value of x (or magnitude if x is complex) 
int(x) 
return the integer constructed from float x by truncating decimal 
len(sequence) 
return the length of the sequence 
sum(sequence) 
return the sum of the entries of sequence 
max(sequence) 
return the maximum value in sequence 
min(sequence) 
return the minimum value in sequence 
range(a,b,step) 
return the range object of integers from a to b (exclusive) by step 
list(sequence) 
return a list constructed from sequence 
sorted(sequence) 
return the sorted list from the items in sequence 
reversed(sequence) 
return the reversed iterator object from the items in sequence 
enumerate(sequence) 
return the enumerate object constructed from sequence 
zip(a,b) 
return an iterator that aggregates items from sequences a and b 
Use the function print()
to display values:
pi = 3.14159
print(pi)
3.14159
Use the function type()
to see the datatype of a value:
type(pi)
float
Use the function abs()
to compute the absolute value of a real number:
x = 2018
abs(x)
2018
Or compute the magnitude of a complex number:
z = 3  4j
abs(z)
5.0
Use the function int()
to truncate a float into an int:
pi = 3.14159
int(pi)
3
The function truncates floats always towards 0:
c = 1.2345
int(c)
1
Use the function len()
to compute the length of a sequence:
primes = [2,3,5,7,11,13,17,19,23,29,31,37,41]
len(primes)
13
Use the function sum()
to compute the sum of a sequence:
one_to_hundred = range(1,101)
sum(one_to_hundred)
5050
Use the functions max()
and min()
to compute the maximum and minimum values in a sequence.
random = [8,27,3,7,6,14,28,19]
print(max(random))
print(min(random))
28
3
Use the function list()
to convert a sequence (such as a range or a tuple) into a list:
list(range(0,10,2))
[0, 2, 4, 6, 8]
Use the function sorted()
to sort a sequence:
sorted_random = sorted(random)
print(random)
print(sorted_random)
[8, 27, 3, 7, 6, 14, 28, 19]
[3, 6, 7, 8, 14, 19, 27, 28]
Use the function reversed()
to reverse the order of a sequence:
reversed_random = list(reversed(random))
print(random)
print(reversed_random)
[8, 27, 3, 7, 6, 14, 28, 19]
[19, 28, 14, 6, 7, 3, 27, 8]
Use the function enumerate()
to enumerate a sequence:
squares = [n**2 for n in range(0,6)]
print(squares)
enum_squares = list(enumerate(squares))
print(enum_squares)
[0, 1, 4, 9, 16, 25]
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
Use the function zip()
to combine sequences into a list of pairs:
random_1 = [2,4,0,5]
random_2 = [7,1,9,3]
random_zip = list(zip(random_1,random_2))
print(random_zip)
[(2, 7), (4, 1), (0, 9), (5, 3)]
Notice in the last three examples reversed()
, enumerate()
and zip()
we use the function list()
to create a list from the output of each function. This is because these functions return iterator objects (similar to range objects) which only yield values when explicitly told to do so.
Defining Functions
Let's begin with a simple example. Define a function which returns the average of a sequence of numbers:
def average(x):
"Compute the average of the values in the sequence x."
sum_x = sum(x)
length_x = len(x)
return sum_x / length_x
The main points to observe are:
 Start the function definition with the
def
keyword.  Follow
def
with the name of the function.  Follow the function name with the list of input parameters separated by commas and within parentheses.
 End the
def
statement with a colon:
.  Indent the body of the function by 4 spaces.
 Use the
return
keyword to specify the output of the function (but it is not always necessary).  The second line is a documentation string (enclosed in quotation marks " ... ") which describes the function.
In Python, code blocks are defined using indentation. This means that lines of code indented the same amount are considered one block. In the example above, the four indented lines below the def
statement form the body of the function.
Notice that there is no output when we execute the cell containing the function definition. This is because we've only defined the function and it's waiting for us to use it! We need to call the function with values for the input parameters and then the function will compute and return the output value.
Let's test our function:
average([1,2,3,4])
2.5
The function returns the expected value. Success!
Documentation Strings
The first line after the def
statement in a function definition should be a documentation string (or docstring). A docstring is text (enclosed in double quotes " ... "
or triple quotes ''' ... '''
) which describes your function. Use triple quotes for a multiline docstring. See the Python documentation for all the conventions related to documentation strings.
A helpful feature of the Jupyter notebook is the question mark operator ?
. This will display the docstring of a function. Keep this in mind when writing your docstrings: other people will read your docstring to learn how to use your function.
For example, use the question mark ?
to view the documentation for the builtin function sum()
:
sum?
I recommend (but it's up to you) a style similar to NumPy's style guide for docstrings:
def function_name(param1,param2,param3):
'''First line is a oneline general summary.
A longer paragraph describing the function and
relevant equations or algorithms used in the
function.
Parameters

param1 : datatype
Describe the parameter.
param2 : datatype
Describe the parameter.
param3 : datatype
Describe the parameters and continue with more
details if necessary on a new set of indented lines.
Returns

datatype
A description of the output of the function and also
describe special behaviour.
Examples

>>> function_name(1,2,3)
1.2345
'''
See these examples and these examples.
Keyword Arguments
When we define functions, we list the input parameters. These are called positional parameters (or positional arguments) because the position in the def
statement determines which parameter is which.
def poly(x,y):
"Compute x + y**2."
return x + y**2
poly(1,2)
5
poly(2,1)
3
A keyword argument allows us to insert default values for some parameters and we call them by name and the order doesn't matter.
def greeting(first_name,last_name,salutation='Hello, '):
return "{0}{1} {2}!".format(salutation, first_name, last_name)
greeting('Patrick','Walls')
'Hello, Patrick Walls!'
greeting('Walls','Patrick')
'Hello, Walls Patrick!'
greeting('LeBron','James',salutation='I love you ')
'I love you LeBron James!'
In this function, first_name
and last_name
are positional arguments and saluation
is a keyword argument.
For example, the function pandas.read_csv
in the pandas
package has many keyword arguments:
import pandas as pd
pd.read_csv?
So many keyword arguments! The keyword arguments I use most often are encoding
, skiprows
and usecols
.
Comments
Comments in a Python program are plain text descriptions of Python code which explain code to the reader. Python will ignore lines which begin with the hash symbol #
and so we use the hash symbol to write comments to explain the steps in a program. See the examples below.
Examples
Area of a Triangle
Let's define a function called area_triangle
which takes an input parameter vertices
which is a list of tuples representing the vertices of a triangle and returns the area of the triangle using Heron's Formula:
$$ A = \sqrt{s(sa)(sb)(sc)} $$
where $a$, $b$ and $c$ are the side lengths and $s$ is the semiperimeter
$$ s = \frac{a+b+c}{2} $$
def area_triangle(vertices):
'''Compute the area of the triangle with given vertices.
Parameters

vertices : list of tuples of numbers
The vertices of a triangle [(x1,y1),(x2,y2),(x3,y3)].
Returns

float
Area of the triangle computed by Heron's formula.
Examples

>>> area_triangle([(0,0),(3,0),(3,4)])
6.0
>>> area_triangle([(1,2),(3,1),(4,1)])
8.499999999999996
'''
# Find the x distance between vertices 0 and 1
a_x = abs(vertices[0][0]  vertices[1][0])
# Find the y distance between vertices 0 and 1
a_y = abs(vertices[0][1]  vertices[1][1])
# Compute length of side a
a = (a_x**2 + a_y**2)**0.5
# Find the x distance between vertices 1 and 2
b_x = abs(vertices[1][0]  vertices[2][0])
# Find the y distance between vertices 1 and 2
b_y = abs(vertices[1][1]  vertices[2][1])
# Compute length of side b
b = (b_x**2 + b_y**2)**0.5
# Find the x distance between vertices 0 and 2
c_x = abs(vertices[0][0]  vertices[2][0])
# Find the y distance between vertices 0 and 2
c_y = abs(vertices[0][1]  vertices[2][1])
# Compute length of side c
c = (c_x**2 + c_y**2)**0.5
# Compute semiperimeter
s = (a + b + c)/2
# Compute area
area = (s*(s  a)*(s  b)*(s  c))**0.5
return area
Let's test our function. We know that the area of a right angle triangle with sides of length 1 and hypotenuse $\sqrt{2}$ has area $0.5$.
area_triangle([(0,0),(0,1),(1,0)])
0.49999999999999983
Let's test again on another triangle with base $b=3$ and height $h=4$ and therefore its area is $A=3(4)/2=6$.
area_triangle([(0,0),(3,0),(1,4)])
6.000000000000003
The function area_triangle
returns the expected values. Success!
Riemann Zeta Function
The Riemann zeta function is the infinite sum
$$ \zeta(s) = \sum_{n = 1}^{\infty} \frac{1}{n^s} $$
Write a function called zeta
which takes 2 input parameters s
and N
and returns the partial sum:
$$ \sum_{n=1}^N \frac{1}{n^s} $$
def zeta(s,N):
"Compute the Nth partial sum of the zeta function at s."
terms = [1/n**s for n in range(1,N+1)]
partial_sum = sum(terms)
return partial_sum
Let's test our function on input values for which we know the result:
zeta(1,1)
1.0
zeta(2,2)
1.25
Now let's use our function to approximate special values of the Riemann zeta function:
$$ \zeta(2) = \frac{\pi^2}{6} \hspace{10mm} \text{and} \hspace{10mm} \zeta(4) = \frac{\pi^4}{90} $$
Compute the partial sum for $s=2$ and $N=100000$:
zeta(2,100000)
1.6449240668982423
Compare to an approximation of the special value $\pi^2/6$:
3.14159265**2/6
1.6449340630890041
Compute the partial sum for $s=4$ and $N=100000$:
zeta(4,100000)
1.082323233710861
Compare to an approximation of the special value $\pi^4/90$:
3.14159265**4/90
1.0823232287641997
Harmonic Mean
Write a function called harmonic_mean
which takes an input parameter s
, a list of numbers $x_1, \dots, x_n$ of length $n$, and returns the harmonic mean of the sequence:
$$ \frac{n}{\frac{1}{x_1} + \frac{1}{x_2} + \cdots + \frac{1}{x_n}} $$
def harmonic_mean(s):
"Compute the harmonic mean of the numbers in the sequence s."
n = len(s)
terms = [1/s[i] for i in range(0,n)]
result = n/sum(terms)
return result
Let's test our function:
harmonic_mean([1,1,1,1])
1.0
harmonic_mean([1,2,3])
1.6363636363636365
Riemann Sums
Write a function called mn_integral
which takes input parameters m
, n
, a
, b
and N
and returns the (right) Riemann sum:
$$
\int_a^b f(x) \, dx \approx \sum_{k=1}^N f(x_k) \Delta x \ \ , \ \ f(x) = \frac{x^m + 1}{x^n + 1}
$$
and $\Delta x = (ba)/N$ and $x_k = a + k \Delta x$.
def mn_integral(m,n,a,b,N):
'''Compute the (right) Riemann sum for the function
f(x) = (x^m + 1)/(x^n + 1) on interval [a,b] with a
partition of N subintervals of equal size.
Parameters

m , n : numbers
Parameters in function f(x) = (x^m + 1)/(x^n + 1)
a , b : numbers
Limits of integration.
N : integer
Size of partition of interval [a,b].
Returns

float
The (right) Riemann sum of f(x) from a to b
using a partition of size N.
Examples

>>> mn_integral(0,1,0,1,2)
1.1666666666666665
>>> mn_integral(1,2,0,1,100000)
1.1319717536649336
'''
# Compute the width of subintervals
delta_x = (b  a)/N
# Create N+1 evenly spaced x values from a to b
x = [a + k*delta_x for k in range(0,N+1)]
# Compute terms of the sum
terms = [(x[k]**m + 1)/(x[k]**n + 1)*delta_x for k in range(1,N+1)]
# Compute the sum
riemann_sum = sum(terms)
return riemann_sum
Let's test our function on input for which we know the result. Let $m=0$, $n=1$, $a=0$, $b=1$ and $N=2$. Then $x_0 = 0$, $x_1 = 1/2$, $x_2 = 1$ and $\Delta x = 1/2$, and we compute:
$$ \begin{aligned} \sum_{k=1}^N f(x_k) \Delta x &= \sum_{k=1}^2 \frac{x_k^0 + 1}{x_k^1 + 1} \Delta x \\ &= \frac{2}{(1/2) + 1} \cdot \frac{1}{2} + \frac{2}{1 + 1} \cdot \frac{1}{2} \\ &= \frac{7}{6} \end{aligned} $$
mn_integral(0,1,0,1,2)
1.1666666666666665
7/6
1.1666666666666667
Let's test our function on another example. Let $m=1$, $n=2$, $a=0$, and $b=1$. We can solve this integral exactly:
$$ \begin{aligned} \int_0^1 \frac{x + 1}{x^2 + 1} dx &= \int_0^1 \frac{x}{x^2 + 1} dx + \int_0^1 \frac{1}{x^2 + 1} dx \\ &= \left. \left( \frac{1}{2} \ln(x^2 + 1) + \arctan x \right) \right_0^1 \\ &= \frac{1}{2} \ln(2) + \frac{\pi}{4} \end{aligned} $$
Approximate this integral with a Riemann sum for $N=100000$:
mn_integral(1,2,0,1,100000)
1.1319717536649336
Since $\pi \approx 3.14159265$ and $\ln(2) \approx 0.69314718$, we compare to the approximation:
0.5*0.69314718 + 3.14159265/4
1.1319717525000002
Our function computes the expected values!
Exercises

Write a function called
power_mean
which takes input parameterssequence
andp
wheresequence
is a list of positive real numbers $x_1, \dots, x_n$ andp
is a nonzero number. The function returns the power mean with exponent p:$$ \left( \frac{1}{n} \sum_{i=1}^n x_i^p \right)^{1/p} $$
Plug in large positive values of $p$ and various lists of numbers to verify
$$ \lim_{p \to \infty} \left( \frac{1}{n} \sum_{i=1}^n x_i^p \right)^{1/p} = \max {x_1, \dots, x_n } $$
Plug in large negative values of $p$ and various lists of numbers to verify
$$ \lim_{p \to \infty} \left( \frac{1}{n} \sum_{i=1}^n x_i^p \right)^{1/p} = \min {x_1, \dots, x_n } $$

Write a function called
arctan_taylor
which takes input parametersx
andN
and return the Taylor polynomial of degree $N$ of the function $\arctan x$ evaluated atx
:$$ \sum_{k=0}^N (1)^k \frac{x^{2k + 1}}{2k + 1} $$

Write a function called
zips
which takes input parametersa
andb
, wherea
andb
are lists of the equal length, and returns the list of tuples which aggregates the sequence. (In other words, write your own version of the builtin functionzip()
... without usingzip()
of course.) For examplezips([1,3,4,0],[5,7,1,9])
returns the list[(1, 5), (3, 7), (4, 1), (0, 9)]
. 
Write a function called
sqrt_integral
which takes input parametersu
,p
andN
and returns the Riemann sum (using the midpoints $x_k^*$ of a partition of size $N$):$$ \int_0^u \frac{1}{\sqrt{1 + x^p}} dx \approx \sum_{k=1}^N \frac{1}{\sqrt{1 + (x_k^*)^p}} \Delta x $$
where $\Delta x = u/N$ and $x_k^* = (x_k + x_{k1})/2$ for endpoints $x_k = k \Delta x$.