So far we have looked how to use variables to perform calculations, loops and conditionals the basic building blocks of programming. But what we would like to do is get to the stage where we can write blocks of functional code to perform a specific task, since cutting and pasting it is going to make our code very long and very repetitive, very quickly. We’d like a way to package our code so that it is easier to re-use, and Python provides for this by letting us define things called 'functions' a shorthand way of re-executing longer pieces of code. Let’s start by defining a function that does what all good first examples should do, display "Hello World!".
def print_greeting():
print('Hello World!')
When we create a function and "run" it, nothing happens. This is sensible because what we want to do is create the function once, and then run it when we want to execute the code. We do this by calling the function:
print_greeting()
This is all very well but it's unlikely that this is a function that we are going to have much use for. Let's write a function fahr_to_celsius
that converts temperatures from Fahrenheit to Celsius, and then we'll examine its structure:
def fahr_to_celsius(temp):
return ((temp - 32) * (5/9))
Let's examine the different parts of our function:
The function definition opens with the keyword def
followed by the name of the function fahr_to_celsius
and a parenthesized list of parameter names temp
. The body of the function - the statements that are executed when it runs - is indented below the definition line. The body concludes with the keyword return
followed by the value the function calculates.
When we call the function, the values we pass to it are assigned to those variables so that we can use them inside the function. Inside the function, we use a return statement to send a result back to whoever asked for it.
Let's try running the function:
fahr_to_celsius(32)
This command should call our function, using “32” as the input and return the function value.
In fact, calling our own function is no different from calling any other function. Throughout this lesson we have been using built-in functions like print()
, type()
, int()
, str()
, range()
, etc. Our new function is no different except that we have defined it ourselves.
print('freezing point of water:', fahr_to_celsius(32), 'C')
print('boiling point of water:', fahr_to_celsius(212), 'C')
We’ve successfully called the function that we defined, and we have access to the value that we returned.
Now that we’ve seen how to turn Fahrenheit into Celsius, we can also write the function to turn Celsius into Kelvin:
def celsius_to_kelvin(temp_c):
return temp_c + 273.15
We can run the function as we did before:
print('freezing point of water in Kelvin:', celsius_to_kelvin(0.))
What about converting Fahrenheit to Kelvin? We could write out the formula from scratch, but we don’t need to. Instead, we can compose the two functions we have already created and test it:
def fahr_to_kelvin(temp_f):
temp_c = fahr_to_celsius(temp_f)
temp_k = celsius_to_kelvin(temp_c)
return temp_k
print('boiling point of water in Kelvin:', fahr_to_kelvin(212.0))
This is our first taste of how larger programs are built: we define basic operations, then combine them in ever-large chunks to get the effect we want. Real-life functions will usually be larger than the ones shown here - typically half a dozen to a few dozen lines - but they shouldn’t ever be much longer than that. Otherwise the next person who reads it (likely to be our future selves!) won’t be able to understand what’s going on.
We have also already seen that it is possible to define default
values. If you recall, when we used range(10)
, 10
is the stop
value of the function, but the function had default values, start = 0
and step = 1
. We can do the same in our own functions and will consider the following example:
def display(a=1, b=2, c=3):
print('a:', a, 'b:', b, 'c:', c)
We first run the function with no parameters:
print('no parameters:')
display()
If we instead call the function with one, two or three values, these are used as the parameters of the function:
print('one parameter:')
display(55)
print('two parameters:')
display(55, 66)
print('three parameters')
display(55,66,77)
This example shows that parameters are matched up from left to right, and any that haven’t been given a value explicitly get their default value.
We can override this behavior by assigning the value we pass in to a specific parameter in our function:
print('only setting the value of c')
display(c=77)
As we suggested earlier it is best to write the functions so that they are readable so that we can more easily understand when we return to them in the future. Consider the following:
def m(p):
a = 0
for v in p:
a += v
m = a / len(p)
return m
def mean(sample):
sample_sum = 0
for value in sample:
sample_sum += value
sample_mean = sample_sum / len(sample)
return sample_mean
The functions m
and mean
are computationally equivalent (they both calculate the sample mean
), but to a human reader, they look very different. You probably found mean
much easier to read and understand than m
.
As this example illustrates, both documentation and a programmer’s coding style combine to determine how easy it is for others to read and understand the programmer’s code. Choosing meaningful variable names and using blank spaces to break the code into logical “chunks” are helpful techniques for producing readable code. This is useful not only for sharing code with others, but also for the original programmer. If you need to revisit code that you wrote months ago and haven’t thought about since then, you will appreciate the value of readable code!
You can also add documentation to your function using a docstring, to inform users what the code does and aid readability:
def mean(sample):
'''
Takes a list of numbers, sample
and returns the mean.
'''
sample_sum = 0
for value in sample:
sample_sum += value
sample_mean = sample_sum / len(sample)
return sample_mean
help(mean)