Functions
A function takes input parameters, executes a series of computations with those inputs and then returns a final output value. Functions gives 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(object) 
return a list constructed from object 
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)
[3, 6, 7, 8, 14, 19, 27, 28]
Use the function reversed()
to reverse the order of a sequence:
list(reversed(random))
[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."
total = sum(x)
number = len(x)
return total / number
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
Docstrings and Comments
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?
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.
Input:
vertices: list of tuples representing vertices of a triangle
Output:
Area of the triangle
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:
area_triangle([(0,0),(3,0),(3,4)])
6.0
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].
Input:
m, n: numbers
a,b: numbers, limits of integration
N: integer, size of partition of interval [a,b]
Output:
Compute 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 each subinterval
delta_x = (b  a)/N
# Create N+1 evenly space x values from a to b
xs = [a + k*delta_x for k in range(0,N+1)]
# Compute terms of the sum
terms = [(xs[k]**m + 1)/(xs[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 is computing 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$.