This notebook aims to build on some of the key principles of Python laid out so far and introduce a fundamental component of writing good code in Python - the function.
Often in Python it is necessary to do the same or similar computations multiple times. Instead of simply writing out this computation in full each time, it is easier to write it as a function once, and then each time the computation has to occur, simply call the function.
There are a few things which are requirements or best practise scenarios for writing a function. The generalised structure of a function is shown below:
def a_function(input_1,input_2):
'''Short string to describe what the function does'''
#Stuff happens inside the function
return output_1,output_2
So what has happened here then? First of all we have used def
to define the name of the function, in the above example as "a_function". Later on in the code, writing a_function(...), will call the function, much as calling the print()
function tells Python to print something out when code is run. In the brackets, (input_1,input_2)
are any initial data, known as arguments, the function needs to know for performing calculations. It isn't always necessary to have any arguments in a function, in which case it could just be defined as a_function()
.
Once the function name and any arguments have been defined, it is often helpful to write at least a one line string describing what the function does. This helps improve the readability of the function, either for someone else who hasn't written the function or for yourself, if some time has passed and you need to look at the function again.
The bulk of the functional computations happen after this, using any arguments defined during function input and arguments defined during the functions output. The final key part of the function is the return
statement at the end - this allows whatever has been calculated in the function that you want to use in the rest of your code to be either printed or assigned to another variable.
There was quite a lot to understand here, so it might be easier to see what an actual function looks like. Below is a function which allows us to obtain the square root of any number. By running the function below, we can load it into the notebooks memory to use later, using Shift + Enter
.
def square_root(number):
'A function which can obtain the square root of a number'
root_number = number**(1/2)
return root_number
Now, this is a pretty simple function, but hopefully it illustrates all the various parts of a well-written function. There is the name defined as square_root, and it requires an argument (number) to work. Below that, there is a description of the function, before the actual calculation occurs, the line root_number = number**(1/2)
which uses the mathematical function of raising a number to the power of a half to find the square root. Finally, the square root of the input number is returned at the end of the function.
How can this function actually be used in Python though? Execute the below segment of code to see usage of it in practise.
root = square_root(9)
print(root)
As can be seen above, we have assigned to the variable "root" the function square_root for the number 9. This is a trivial example, but we can use our knowledge of for loops from the previous notebook to find out the square roots of a lot of very big numbers all in one go.
Let's say we need to find out the square roots of all the numbers between 1,000,000 and 2,000,000 at intervals of 100,000, printing the value for each one. This is an easy task to achieve using the square_root function and a for loop. Try implementing this below:
for i in range(1000000,2000000,100000):
root = square_root(i)
print('square root of', i, ':', root)
Hopefully, you can now see that the square root of 1,600,000, for example, is 1264.9110640673518. This might not be particularly relevant right now, but if you understand all the parts which make a good function, then it is much easier to write useful functions later.
Another important aspect of Python is using methods of functions or classes to solve problems which often occur in a simple way.
This can be in basic class additions like .extend()
or .append()
which are methods which can be applied to lists. For example say you have the list, a_long_list
as shown in the code below. Often when coding with Python, it is useful to add data to the end of lists. With long lists (or multi-dimensional lists which we will come to soon), it is not practical to just restate the list with the additional values on the end.
This is where the .extend()
or .append()
methods come in useful, with both doing similar but slightly different things. The easiest way to see this is by running the example below, where a_long_list has the string 'new' attached to the end of it.
a_long_list = ['this','is','a','very','very','very','long','list']
a_long_list.append('new')
print(a_long_list)
a_long_list = ['this','is','a','very','very','very','long','list']
a_long_list.extend('new')
print(a_long_list)
As can be seen above, both methods add the word 'new' to the end of the list, but in different ways. The .append()
statement added the entire string 'new' to the end of the list, while the .extend()
statement split the string 'new' into its individual letters for adding to the end of the list.
There is some slightly different behaviour for these methods when using a list of numbers rather than a list of strings, which can be seen by running the example below:
numerical_list = [0,1,2,3,4,5,6,7,8,9]
numerical_list.append(10)
print(numerical_list)
numerical_list = [0,1,2,3,4,5,6,7,8,9]
numerical_list.extend([10])
print(numerical_list)
As can be seen here, for a numerical list, to get the same output, for the .extend()
method, it is necessary to put the value inside a list. Therefore, it is recommended to use the .append()
method when adding single numbers or strings to the end of a list. However, if you would like to add a list of numbers into the end of a list there is a different behaviour, which can be seen by running the example code below:
numerical_list = [0,1,2,3,4,5,6,7,8,9]
numerical_list.append([10,11])
print(numerical_list)
numerical_list = [0,1,2,3,4,5,6,7,8,9]
numerical_list.extend([10,11])
print(numerical_list)
So in this instance, the .append()
method adds the entire list into the end of "numerical_list" as a nested list, while the .extend()
method adds the individual values into "numerical_list", maintaining the single list structure. Both methods have uses, so it's good to know the full usage of both functions.
Another method which can be very useful, is the .split()
method, which can be used in the following example to split a very long string into a list of shorter strings. Running the following code snippet can show this behaviour:
long_string = 'hello this is a long string'
long_string.split()
This can be very helpful in input operations for large .txt or .csv files into Python, which allows for getting data into Python in a useful format.
Finally, let's put all of this together. Try in the box below to split up the long string currently there, assign it to a variable and then add this list to the end of "a_long_list".
long_string = 'this string needs to be added to the end of a list!'
split_string = long_string.split()
a_long_list.extend(split_string)
print(a_long_list)
long_string = 'this string needs to be added to the end of a list!'
These are just a couple of examples of all the methods which can be used in Python. Click the following links for more information about all the methods available for strings & lists in Python.
Additionally, this is a good point to say that the internet has an astonishing array of information regarding solving problems with Python. If you are struggling with something in Python, type it into a search browser like Ecosia, and often you'll find the answer to your problem or a related one.