In-class worksheet 16

Mar 12, 2020

Introduction to functions in Python

Python, like all computer languages, contains many built-in functions, such as print(), range(), len(). These functions carry out commonly-used tasks. Because most programming problems have their own commonly-used tasks specific to the given problem, it is often necessary to write custom functions. In fact, functions are an integral part of programming, allowing your code to be modular, repeatable, and readable.

In Python, functions are defined with the def command. Similar to if and for, code following the def command must be indented. Functions generally take arguments as input and return results from the computation as output. To end the function and return a result, we use the command return.

The basic format for defining a function is:

def my_function(argument1, argument2, ...):
    code 
    code
    more code
    return x

Note that the variable names (above, argument1 and argument2) are completely arbitrary and exist only within the code of the function itself.

Here is an example of a simple function to add two numbers together:

In [1]:
# Define a function called `add`. 
# This function takes 2 arguments and returns their sum.
def add(x, y):
    return x + y

# To execute the code inside the function, run the function:
print(add(4, 99))

# We can also use variables as input to the function:
number1 = 4
number2 = 99
print(add(number1, number2))
103
103

Variables that are defined inside of functions do not exist outside of the function. For example, in this case, the function add defines a variable result but this does not change the value of the variable result outside of the function:

In [2]:
result = 15 # define variable `result`

def add(x, y): # define function `add`, which uses a variable `result`
    result = x + y
    return result

print(result) # result is 15
add(1, 2) # add numbers 1 and 2
print(result) # result still 15
15
15

Function arguments can have default values, in which case they don't need to be provided when the function is called:

In [3]:
# Use 0 as default value for y. If it is not given, the add
# function adds nothing:
def add(x, y = 0):
    result = x + y
    return result

print(add(5))    # 5
print(add(5, 2)) # 7
5
7

The order of arguments matters. Arguments need to be provided in the order in which they are listed in the function definition. As an example, consider a simple function that divides two numbers. Obviously, it matters which number is the numerator and which the denominator:

In [4]:
# Simple divide function
def divide(numerator, denominator):
    return numerator / denominator

print(divide(10, 5)) # 10/5 = 2
print(divide(5, 10)) # 5/10 = 0.5
2.0
0.5

Functions can return more than one result at once. This is done by separating the results by commas in the return statement:

In [5]:
# Define a function to divide two numbers.
# The function returns both the dividend and remainder:
def divide_remain(x, y):
    div = x / y
    rem = x % y     # % is the modulo operator which returns the remainder
    return div, rem # return multiple values separated by commas

# Run the function and save returned values
a = 88.0
b = 12.0
# Returned values can be saved to unique variables
ratio, remainder = divide_remain(a, b)
print(ratio)
print(remainder)
7.333333333333333
4.0
In [6]:
# Alternatively, returned values can be saved to a single variable
# and then indexed
results = divide_remain(a, b)

print(results[0])
print(results[1])
7.333333333333333
4.0

Problems

Problem 1:

Write a function to convert between degrees Celsius and Fahrenheit. The formulas are as follows:

  • C = (F - 32) * 5/9
  • F = 9/5 C + 32

(a) Write a function convert_to_Celsius that takes a temperature in Fahrenheit as argument and returns the equivalent temperature in Celsius. Test that your function converts 32 F to 0 C and 212 F to 100 C.

(b) Now write a function convert_temperature that can convert both Fahrenheit to Celsius and Celsius to Fahrenheit. This function should take two arguments, the temperature and the target unit (Celsius or Fahrenheit). Your function should return a single variable, the new converted temperature. Test that your function converts 0 C to 32 F and vice versa, and 100 C to 212 F and vice versa.

In [7]:
# Code for Problem 1a goes here.

def convert_to_Celsius(temp):
    return (temp - 32.) * 5./9.

print("32 F in C:", convert_to_Celsius(32))
print("212 F in C:", convert_to_Celsius(212))
32 F in C: 0.0
212 F in C: 100.0
In [8]:
# Code for Problem 1b goes here.

def convert_temperature(temp, target_unit = 'f'):
    # we first convert the unit string to lower case, so we 
    # need to test fewer cases in the following `if` statement
    target_unit = target_unit.lower()
    if target_unit == 'c' or target_unit == 'celsius':
        return (temp - 32.) * 5./9.
    else:                 # convert from C to F by default
        return (9./5.) * temp + 32.
    
print("0C in F:", convert_temperature(0, "F"))
print("32F in C:", convert_temperature(32, "C"))
print("100C in F:", convert_temperature(100, "F"))
print("212F in C:", convert_temperature(212, "C"))
0C in F: 32.0
32F in C: 0.0
100C in F: 212.0
212F in C: 100.0

Problem 2:

This piece of code sums all multiples of 3 between 1 and 30:

result = 0              # our final result; we start with 0
for i in range(1, 31):  # count from 1 to 30
    if i % 3 == 0:        # test if number can be divided by 3
        result += i     # add number to result
print("The sum of all multiples of 3 which are <= 30 is", result)

Using this code as a template, write a function that can calculate all multiples of a given number, for all values within a specified range. This function should take three arguments: the multiple (3 in example above), the lower limit (1 in example above), and the upper limit (30 in example above). The function should return a single value, the sum of all multiples in that range.

In [9]:
# Code for Problem 2 goes here.

def sum_of_multiples(multiple, lower, upper):
    result = 0
    for i in range(lower, upper):
        if i % multiple == 0:
            result += i
    return result

print(sum_of_multiples(3, 1, 31))
165

Problem 3:

a) Write a function called calc_mol_weight to calculate the molecular weight of an amino acid sequence. Your function should take a single argument, an amino acid sequence, and return a single value, the sequence's weight. In your function, use the amino_weights dictionary given below.

b) Test that your function behaves properly with the two protseq variables defined below. The known molecular weights for each protseq variable are shown in the comments.

In [10]:
# Your function goes here.

def calc_mol_weight(protseq):
    amino_weights = {'A':89.09,'R':174.20,'N':132.12,'D':133.10,
                     'C':121.15,'Q':146.15,'E':147.13,'G':75.07,
                     'H':155.16,'I':131.17,'L':131.17,'K':146.19,
                     'M':149.21,'F':165.19,'P':115.13,'S':105.09,
                     'T':119.12,'W':204.23,'Y':181.19,'V':117.15}
    weight = 0
    for aa in protseq:
        if aa in amino_weights:
            weight += amino_weights[aa]
    return weight

# Variables for testing that the function works properly.
protseq1 = "AGAHHCTPL" # weight = 1050.14
protseq2 = "HQWRSSXAD" # weight = 1112.11

# Run the function on protseq1 and protseq2.
print(calc_mol_weight(protseq1))
print(calc_mol_weight(protseq2))
1050.1399999999999
1112.1100000000001

If this was easy

Problem 4:

For Problem 3 above, write code that automatically tests whether the calc_mol_weight function returns the correct result for protseq1 and protseq2.

In [11]:
# this doesn't work because of rounding errors
print("Incorrect implementation of test:")
if calc_mol_weight(protseq1) == 1050.14:
    print("  result correct for protseq1")
else:
    print("  result incorrect for protseq1")
if calc_mol_weight(protseq2) == 1112.11:
    print("  result correct for protseq3")
else:
    print("  result incorrect for protseq2")

# we need to test whether they fall into a given small range of values:
print("Correct implementation of test:")
if abs(calc_mol_weight(protseq1) - 1050.14) < 0.00001:
    print("  result correct for protseq1")
else:
    print("  result incorrect for protseq1")
if abs(calc_mol_weight(protseq2) - 1112.11) < 0.00001:
    print("  result correct for protseq2")
else:
    print("  result incorrect for protseq2")    
Incorrect implementation of test:
  result incorrect for protseq1
  result incorrect for protseq2
Correct implementation of test:
  result correct for protseq1
  result correct for protseq2

Problem 5:

The Fibonacci Sequence is the sequence of numbers, starting with 0 and 1, where the next number is the sum of the two previous numbers:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...

Mathematically, we can write the $i$th number $n_i$ as

$n_i = n_{i-1} + n_{i-2}$.

Implement a function that calculates the ith Fibonacci number. (Hint: this is easiest by making the function call itself to obtain the previous Fibonacci numbers. This programming style is called "recursion".) Then use this function to print out the first 20 Fibonacci numbers.

In [12]:
def fibonacci(i):
    if i <= 1:   # the first Fibonacci number is 0. We use <= instead of == to guard against bad input (e.g., i=0.5)
        return 0
    if i <= 2:   # the second Fibonacci number is 1. We use <= instead of == for safety, as before
        return 1
    return fibonacci(i-1) + fibonacci(i-2)# the next Fibonacci number is the sum of the two previous ones.

# print the first 20 fibonacci numbers
for i in range(1, 20):
    print(fibonacci(i))
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584