Session 1 Basic Concepts in Programming and Python

In our first session, we will cover the basics of programming. These concepts include the notion of a program or script, the key concept of variables and their type. We will also look at the key concept of a for loop in programming. We will finish by examining how we can make the output that we generate neat by using f-strings.

1.1 Learning Points

Concepts:

  • Comments
  • Variables
  • Outputting to the screen
  • The type of data
  • Data types: str, int, float and bool
  • Writing scripts
  • Collections types: list and tuple
  • Loops: for
  • Formatting Strings: f-strings

Functions, Classes and Keywords:

  • bool
  • False
  • float
  • for
  • in
  • int
  • len
  • list
  • None
  • print
  • range
  • str
  • tuple
  • type
  • True

Operators:

  • =: assignment
  • ==: equality comparison
  • +: addition
  • -: subtraction
  • *: multiplication
  • /: division
  • //: integer division
  • **: exponentiation (power)
  • %: remainder
  • (): function calls

1.2 What is Programming?

Computers can perform many tasks – in fact, any task for which we can formally write down the rules. The art of programming is simply working out what we want to happen and then writing it down in a manner in which a computer understands.

Programming is now an essential tool for scientists. The time when a scientist could come up with an idea and then ask someone else to implement it is now over – you need to become self-reliant! (Not to mention the fact that you really should know how to check what someone else has done for you…). There are many uses of programming – the two primary ones which we will investigate in this course are stimulus presentation and basic data analysis. There is a lot more to it than this however. Along the way, we will try to take time to point out where programming can save you time and reduce mistakes in various areas of your work. Like most things, the more you practice programming, the more fluent and efficient you will be. By the end of the course, we hope that if you find yourself loading 20 data files into a spreadsheet and manually calculating values, you will think “there is a better way to do this” and be able to implement it.

There are many (hundreds of) languages available to talk to computers. Common ones in scientific use (which we use at YNiC) include MATLAB, C, R and of course Python.

1.2.1 Interpreters and Scripts

When using an interpreted computer language (e.g. Python / MATLAB), the interpreter reads the commands that we give the computer and makes the relevant actions happen.

We can type these commands in one at a time directly into the interpreter; this is great for testing an idea or doing something simple and quick but for a long and complex program, it is not feasible.

For longer programs, we put our commands into one or more files and then tell the interpreter to read the commands from there. Note that when we are writing scripts in this way, we need to use a plain text editor (e.g. notepad, gedit, vim, the editor built into spyder3 or MATLAB) etc – you must not try to use Word or something similar!1 For this course, we will stick to using the editor built into either Spyder3 (or in sessions 5 and 6, Psychopy). If you are working in Colaboratory, the text editor is just built into the webpage as a ‘code section’.

1.2.2 Spyder3 - setting up

In this course, we will be using a graphical interface to Python called Spyder3. On the YNiC systems, this can be found in Applications → Development → Spyder3

If you want to install it on your own computer, you can find it here: https://www.spyder-ide.org/

Be very careful to launch “Spyder 3” and not “Spyder”. You can tell the difference by looking at the bottom right of the screen. If the iPython console starts with Python 2.7.16, you have started the wrong version - it should start with Python 3.2

Default Spyder layout with script editor on left, Help and Debugging panel on the top right and the iPython console on the bottom right.

Default Spyder layout with script editor on left, Help and Debugging panel on the top right and the iPython console on the bottom right.

We need to configure a couple of items the first time we start spyder. Go to Tools → Preferences and change the following options: * Run * Choose “Execute in a dedicated console” * This means that we start a new instance of python (a ‘shell’) for every time we run our scripts. It stops us breaking things from previous runs! * Ipython console → Graphics Tab * Change graphics backend to Automatic * If we do not do this, it will display plots inline in the shell as we type them – we do not want that, we want them as separate windows

1.2.3 Getting Started with iPython3

Most of the examples in this document are written to show the python code you will type and the results which will be produced.

Here is our first example:

Hello World

The first area (with the line numbers) shows the code as you type it. Note that you should not include the line numbers when typing the code in - the line numbers are simply present for reference.

The second area shows the output of the program. This should be displayed in your terminal or iPython3 console. In Colab (or some other notebook) it will appear below your code block.

In the iPython3 console, you will see that your line starts with:

In [1]:

This is a prompt that is asking you to type your first command. Type the code from the example above and then press enter. You should see the following:

An example of running the print(“Hello World”) example above in the iPython console

An example of running the print(“Hello World”) example above in the iPython console

The number in square brackets is simply the number of lines you have typed. You can ignore this.

Later on in the tutorials, we will move on to writing scripts in files and running them from python3 or ipython3. We will discuss this when we get there. From now on, we will refer to ipython3 as simply ipython.

1.2.4 ipython commands

The commands in this session are not part of the python language and you can not use them in scripts or real code. These are instructions to ipython and so are prefixed with the % character. In the main, they are shortcuts which make life easier when you are working interactively (rather than when you are writing a script). Many of these commands will be familiar to those who have used UNIX-based systems before.

We can start by finding our Present Working Directory (%pwd). If you try and open a file without a full path, this is where python will look:

'/home/mark'

We can change our directory using %cd:

'/home/mark/ynic'

If we then use %pwd again, we find that our present working directory has changed (as we would expect):

'/home/mark/ynic'

%history shows the commands you have typed in this session. More advanced versions can show you code that you typed in previous sessions. The output is not shown to save space.

%magic lists the other built-in ipython commands. Use the scroll keys to scroll through this text and press q to stop viewing the text. The output is not shown here.

The %run command will run a script inside the ipython session. We can use this later on when we write code in a file and want to run it. Don’t run this as you haven’t yet made a script to run.

Finally, the help command (which, unlike the other commands in this section is part of Python itself rather than iPython) will show the help appropriate to the passed argument. Here, we ask for help on the string type (we will explain this later on). Again, use q to quit the help screen.

Some of these commands are also available in Colab and some are not. You can find out which ones work by trial and error.

1.2.5 Quick Exercise

Practice typing commands in your iPython console:

  • Check what your present working directory is and make a note of it (this will be your home directory on the system on which you are working)
  • Change to another directory (you may need to create another directory in your home directory to do this) and check that the present working directory changes.
  • Look up the help for the print function and read it - don’t worry too much if you don’t understand all of the details for now.

1.3 Comments

Comments are parts of your code that the computer ignores. This allows you to add notes, explanations or documentation to your program in English so that the person reading the script will understand what was happening.

The comment character in Python is the hash: #.3 We can use the hash at any point in a line and the rest of the line will be ignored:

Comments should be written in English with good punctuation, grammar and capitalisation4. Having said that, we often leave off the full stop of a single sentence and sentence fragments are sometimes used. Remember that with comments you are communicating with the person reading the script; do so in a way in which you would in any other written document, whilst remembering that you are not writing an essay! We will talk more about comments in future sessions.5

Many of the code examples below include comments - these are there to help you follow the code. If there is a “comment” in the example output, it will not appear on your screen - it is just there to help orient you in the longer output sections.

1.4 Hello World and Strings

Let us start traditionally by printing “Hello World” to the screen many times to illustrate some basic string handling and printing behaviour of Python:

Hello World

The print function is how we output things to the terminal. We pass the print function a string which is the text we want to put onto the screen. Strings can be created using either the double quote " or single quote characters '..6 So, for instance, using the single quote ' will have the same effect as the first example:

Hello World

The reason that there are two variants is that if you want to include a single quote in a string, the easiest way is to create the string using double quotes (") - you can then use the single quote in the string. The same applies the other way around.

Hello 'World'

Being able to simply print what we have written to the screen does not seem useful, but it does allow us to start to demonstrate how we can manipulate data within Python.

For instance, we can add two or more strings together. This simply concatenates them.

HelloWorld

At this point, we notice that there is no space between the words. This is unsurprising as at no point did we tell Python that it needed to include a space. We therefore need to include a space, either in the first string, the second string or even as a third string in the middle (all three examples follow):

Hello World
Hello World
Hello World

Note that all three give the same output.

As well as simply printing a string or concatenating some strings together, we can manipulate the data before we output it.

For our first example, we use len on a string to find out how many characters are in it.

11

len is an example of a function. We will discuss functions more in the next session, but the concept is that a functions takes arguments (sometimes known as parameters) and then gives you back (returns) something. In the case of len, the function takes a single argument and works out what the length is. It then returns this length as a number which we can either print, put in a variable or otherwise use as appropriate. Later, we will see that len can do other things depending on what we pass to it - it will automatically choose the right behaviour based on its input type.

In this case, we have passed a string ("Hello World") to the len function. The len function will calculate the number of characters in the string and then return it. This value will then be passed into print.

Note that we use two levels of brackets in the example. If we lay this out differently, you may find it easier to follow. This is the code we start with:

print(
      len(
          "Hello World"
         )
     )

Python then evaluates the inner-most part of the code. In this case, that is the len function call. It then ends up with this:

print(
      11
     )

It will then evaluate the print function call. The print call is intelligent enough to know how to take the number 11 and turn it into a string. We will talk about the difference between numbers and strings in the next two sections.

1.4.1 Quick Exercise

Put your name into a string variable. Print the variable to the screen and then calculate and print the length of your name.

1.5 Variables

The concept of a variable is key to programming. A variable is simply a name for a value (or, in Python, objects which we will cover later).

We start by reprising our example from before, but using a variable which we will call msg:

Hello World

In this example, we created the a variable called msg. We assign variables using =. We made this variable contain our string "Hello World". We then pass the variable into the print function which gives us the same result as before.

Python’s rules for variable naming are relatively flexible. Variable names can use letters, numbers and underscores. The names are case sensitive, so the variable msg is not the same as the variable Msg - they are two different variables so you need to be precise in your naming.

There are some rules about variables names. First of all, whilst a name can have a number in it, it cannot start with a number: myvar2 is a valid variable name but 2myvar is not. Second, you should avoid naming variables after Python keywords and standard functions. We will learn keywords and function names (such as list, dict, str, file, len etc) as we go. A full reference list of keywords can be found at https://docs.python.org/3/reference/lexical_analysis.html#keywords. The list of built-in functions and constants can be found at https://docs.python.org/3/library/functions.html#built-in-funcs and https://docs.python.org/3/library/constants.html#built-in-consts respectively.

Stylistically, there are two common ways of variable naming:

  • CamelCase7: Where you capitalise the words in the variable name, e.g.: MyVariable
  • use_underscores: Where you use underscores between the words in the variable name, e.g.: my_variable.

In Python, the two styles tend to be used for different parts of the code, there are a whole set of rules and guidelines about this called PEP8. We will not insist on the use of PEP8 in this course, but it is worth reading the document to understand what is considered good style. In this course, as we will rarely be dealing with creating our own classes, we will stick to the use_underscores variant.

Read the following code - what do you expect to happen? What result do you get when you run it?

As demonstrated here, we can overwrite variables with new values. Once we do that, there is no way of going back in our script - we have lost the original value.

We can use as many variables as we like in our scripts.8 In the following example we set:

  • the variable a to be the integer 3
  • the variable b to be the integer 2

and we then demonstrate some basic arithmetic using these numbers:

6
5

We can also test whether a and b are the same number. Be careful to note that we are using two equals signs here, not one: ==:

False

This is known as an equality comparison. We can also use != to test whether two variables are not the same:

True

In this case, as a is 3 and b is 2, they are not equal, so we get True as our response.

As noted earlier, we can re-assign the values which variables contain at any point:

# (a, b)
2 2

# (a == b)
True

On line 1, we reassign the value of a to be the value which is currently contained in b. To demonstrate this, on line 2 we print out the contents of both variables and on line 3 we check whether the values are equal (which they should be at this point). Note that the “comments” in the output will not appear - they are there to orient you as to which line of the output relates back to which line of code.

1.5.1 Examining Variables in Spyder

Spyder has a helpful interface so that you can examine what variables you currently have and what they are set to. Look at the top right of the screen and choose the Variable Explorer. You should see that you have variables called a and b and that they are currently both integers set to 2.

An example of the Spyder variable explorer

An example of the Spyder variable explorer

Note: this is an example of where a ‘real’ programming environment is better than the Colab notebook. Colab can have a variable explorer but it’s a bit of a pain to set up.

1.5.2 Deleting Variables

You will very rarely need to do this, but you can delete a variable using the del built-in:

We can delete the b variable this way. Observe what then happens in the Variable Explorer screen.

1.5.3 Quick Exercise

  • Set a and b to different values and examining how they change in the Variable Explorer.
  • Create a new variable containing a string and observe how the variable explorer shows it.
  • Delete one of the variables which you created - observe that is disappears from the variable explorer.

1.6 Data Types

Variables can be of different types and it is important to understand what this means. The example we used in the previous section used variables of type integer (whole numbers).

The type of a variable tells you what the computer thinks it is. This is important because it does not make sense, for example, to multiply two strings, even if the content of the string is "3.0".

The important types which you will need to know are:

  • Numbers (we will discuss a larger range of number types later on):
    • int: Integer (i.e. a whole number – can be positive or negative: ... -2, -1, 0, 1, 2 ...)
    • float: Floating point (i.e. a decimal number: e.g. 3.14159)
  • str: String – zero or more characters (e.g. “Hello World”)
  • bool: Boolean value – True or False
  • None: None type – this is a special name for nothing in Python which is useful in various contexts.

We can use the type function to examine either a variable or a literal ( i.e. a value we have just typed in). In the following examples, we examine what the computer thinks the type of various _literal_s is as we type them. You can run these lines one at a time or all together:

# print(type(3))
<class 'int'>

# print('Type of type(3) is', type(3))
Type of type(3) is <class 'int'>

# print('Type of type(3.0) is', type(3.0))
Type of type(3.0) is <class 'float'>

# print('Type of type("3.0") is', type("3.0"))
Type of type("3.0") is <class 'str'>

# print('Type of type(None) is', type(None))
Type of type(None) is <class 'NoneType'>

# print('Type of type(True) is', type(True))
# print('Type of type(False) is', type(False))
Type of type(True) is <class 'bool'>
Type of type(False) is <class 'bool'>

On the first line, we just print the return value of the type function. To make things clearer, on the other lines we add some extra information to help with context.

We can repeat the same exercise, but this time using variables:

Type of a is <class 'int'>

Type of b is <class 'float'>

Type of c is <class 'str'>

This means that if we have a variable in a script which we do not know the type of (for example, something that was returned from a function which we did not write), we can ask the script to tell us using the type function.

Note that python makes numbers int by default unless they have a decimal point in them. Much of the time you will want float numbers so it is good practice to add a .0 to the end. As we will see in a moment, this is less important in Python3 than it was in Python2. I still recommend being clear as to whether you intended to use an int or float when typing them by adding .0 to numbers intended to be floating point.

An alternative to adding .0 to the number is to pass it through the float function. You can also do this in reverse, turning a floating point number back into an integer using int - this will lose any floating point part of the number. Note that this does not “round” the number properly, it simply truncates it.9

<class 'float'>

<class 'int'>

3

1.6.1 Numbers

Now that we have understood some basic concepts, we can manipulate numbers in python. We are going to use the addition (+), subtraction (-), multiplication (*), division (/), integer division (//) power (**) and remainder (%) operators

We start with some basic addition and subtraction:

# 3.0 + 3
6.0

# 3 - 2
1

We can then look at multiplication. Take notice of the difference between the two example lines here - what type are the resulting values for each operation?

# 3 * 2
6.0

# 3.0 * 2
6

The power operator (**) can be used to compute both “normal” power values and also, when the exponent is fractional, things like square roots (when the exponent is 0.5):

# 3.0 ** 3
27.0

# 3.0 ** 0.5
1.7320508075688772

In Python 3, there are two variants of division. The first one (/) allows integers to turn into floating point numbers where necessary. The alternative integer division operator (//) does not:

# 3 / 2
1.5

# 3 // 2
1

When using the integer division operator on floating point numbers, the division will be performed as though the numbers were integer, but the result will be floating point:

1.0

As our final operator in this section, we have remainder (%):

1

Finally, an explanation as to why the type of data is important. If we try and add a string and an integer, the computer will refuse:

TypeError                                 Traceback (most recent call last)
----> 1 print("3.0" + 3)

TypeError: can only concatenate str (not "int") to str

Adding a string and an integer does not make any sense and we get an exception. This just means an error - an exceptional event occurred that the computer cannot cope with. The traceback (information in the exception) will normally give you a good hint as to what is wrong. In this case, it tells you that you can only concatenate strings to strings - adding an integer on doesn’t make any sense.

1.6.2 Quick Exercise

Set up a variable age_in_years and give it the value of your age. Assume that there are 365 days per year for now.10 Set up a variable days_per_year with the right value in it. Print out something like:

My age in years is: 25
My age in days is: 9125

Check the variable explorer as you go to ensure that you understand how the variable assignments are working.

1.6.3 Precedence

The rules for precedence of mathematical operations in Python follow roughly the same rules as those in mathematics.

So, for instance: 1 + 2 * 3 will be evaluated as 1 + (2 * 3) giving a result of 7, because multiplication is higher precedence than addition.

In cases where precedence is not correct, or you simply want to make your code clearer, you can use brackets in the same way that you would in algebra. So, for instance you can write the above as:

Brackets can also be nested if necessary:

The full rules on operator precedence in Python can be found at: https://docs.python.org/3/reference/expressions.html#operator-precedence. You may not recognise all of the operators in this list - we will cover most (but not all of them) later in the course.

1.7 Python Scripts

Up until this point, we have worked directly in the ipython console inside of Spyder. This is useful for testing out ideas but we want to be able to save our work and then use it many times. To do this, we put our lines of code into a script.

Scripts are just lines of code which run as if you had typed them in. To create a script, go to File → New File in Spyder and a new script will be created:

New Python script created by Spyder3.

New Python script created by Spyder3.

A script is just a normal plain text file, so you can now enter code:

Example code entered into a script in Spyder3.

Example code entered into a script in Spyder3.

Note that in this case, nothing happens as we type the code in. Before we can run the script, we must first Save it. Go to File → Save and save your script as myscript1.py (or whatever name you prefer, although it should end with .py).

You can now click the Run button (green arrow: Run Button) at the top of the screen or go to Run → Run

The script will run in a new Python interpreter and, if you have printed any output to the screen, it will appear in that window (at the bottom right of your screen):

Results of running a script in Spyder3

Results of running a script in Spyder3

Note that our script printed Hello World and then the result of multiplying 100 by 50.

Once you have done this, try adding some more lines of code to your script, save it and re-run it. Get used to the idea that you will write scripts in small pieces and test them as you go. You generally should not try and write 1000 lines of code without ever trying to run something!

When working on the in-course exercises, you should produce a single python script for each exercise and save it. Bring your scripts along to the tutorial sessions and we will discuss the answers as well as the ideas and concepts behind the work you have done.

To get back to your ipython console, click “Console 1/A” at the top of this window. Try to remember to do your rough working in a different console from the one in which you perform your scripting tests.

1.8 Collections of Data: Lists

Having variables which can be single numbers or strings is useful, but we quite often want to deal with more than a single piece of data. For example, we might have a list of reaction times, or a list containing the names of people. Alternatively, we might have information such as “this person has this phone number” for a lot of people.

To store collections of data like this, we have access to a set of variable types designed to make it easy. The trick is learning which one to use at which time. In Python, you will normally choose between a list and a dictionary (which we will cover in the next session).

1.8.1 Creating and adding items to lists

Lists are pretty much what they say. An ordered list of elements which we can add to, remove from and look into. Note that in python, elements in a list do not have to have the same type.

To create a list, we use square brackets: [ ]. We can start by creating an empty list:

# print(my_list)
[]

# print(type(my_list))
<class 'list'>

You will see that when we print the list, the square brackets are used to indicate to us that we are looking at a list in the output. As before, using type shows us the type of the variable - unsurprisingly, it is a list.

We can now add some elements into our list using the append member function:

# print(my_list)
[100, 105, 120]

# print(len(my_list))
3

append adds a single value into the list. By calling it repeatedly, we keep adding a new element each time and end up with a list with three element in it. As with strings, we can use len to look at the length of the variable - in this case, showing us that we have three element in the list.

We can also create a list which contains element from the start using commas to separate the elements. Note that here we demonstrate that lists can contain data of different types - the first two elements are strings, and the next two are integers. As usual, our strings have to be declared using quotes (in this case we used ', but we could equally well have used ").

# print(my_new_list)
['ant', 'bear', 10, 20]

# print(len(my_new_list))
4

As we noted above, lists can contain any types of data - including other lists. It is natural to wonder what happens if we append a list into another list:

# print(my_main_list)
[1, 2, 3, [10, 20]]

# print(len(my_main_list))
4

# print(what is it)
[10, 20]

# print(type(what_is_it))
<class 'list'>

We see that appending the list into the list has added one new element That element it itself a list and we can extract it as normal using the square bracket syntax. Whilst this is sometimes useful, we also often want to add each element in our new list into our existing list. To do this, we can use the .extend member function:

# print(my_main_list)
[1, 2, 3, 10, 20]

# print(len(my_main_list))
5

Now we can see that we have individually added each element into the main list.

1.8.2 Extracting items from lists

Once we have a list, one of the obvious things that we want to be able to do is to access individual elements of the list. Python is a 0-indexed language. This means that the first element in any sequence (including lists) is called 0 rather than 111. This means that the valid indices for our list of length 4 are: 0, 1, 2, 3. We use these indices with the square brackets [NUMBER] to extract our values. As an example, we can extract the second-to-last element in the list:

# print(my_new_list[2])
10

# print(my_element)
10

# print(type(my_element))
<class 'int'>

We can either extract the element from the list and pass it straight to print (or any other function). Alternatively, we can extract the element and place it into a variable (called my_element in this case). The type of the element of the list is the type of the data within it, in this case an integer. We therefore see that lists are just a container for whatever is in the list.

The square bracket indexing syntax can also be used to modify an element in a list:

# print(my_new_list)
['ant', 'bear', 10, 20]

# print(my_new_list) after we modify the list
['ant', 'bear', 'Hello', 20]

You can see that we changed the third element (using the [2] index) by assigning a new value to it.

Finally, note that Python wraps indexing around with negative numbers. You can therefore access the last element in a list by using -1 as your index, the second last as -2 etc:

# print(my_new_list[-1])
20

# print(my_new_list[-2])
10

1.8.3 Extracting multiple items from lists

As well as extracting single elements from lists, we can extract a number of elements in one go. Unsurprisingly, this gives us another list - albeit one which is shorter.

The indexing notation for extracting multiple elements uses a colon :. The pattern is [start:stop], but as Python is 0-indexed and end-points are exclusive, the element which has the last number will not be included. We will come across this again when we discuss the range function later in this session and numpy in a future session.

Here are some examples:

# print(my_new_list[1:3])
['bear', 10]

# print(len(my_new_list[1:3]))
2

# print(type(my_new_list[1:3]))
<class 'list'>

# print(my_new_list[:2])
['ant', 'bear']

# print(my_new_list[2:])
[10, 20]

# print(my_new_list[1:1])
[]

As before, whilst we have just printed out the returned data here, we could instead assign it to a variable:

['ant', 'bear']

There is also one other option. Indexing can also be specified as [start:stop:step] (again, this will match with our discussion of range later on).

# print(animals[1:5:2])
# The returned indices are 1, 3 (not 5 as stop is exclusive)
['bear', 'deer']

# print(animals[:5:2])
# The returned indices are 0, 2, 4
['ant', 'cat', 'elk']

# print(animals[1::2])
# The returned indices are 1, 3, 5, 7
['bear', 'deer', 'frog', 'horse']

# print(animals[::2])
# The returned indices are 0, 2, 4, 6
['ant', 'cat', 'elk', 'goose']

# print(animals[::-1])
# The returned indices are 7, 6, 5, 4, 3, 2, 1, 0
['horse', 'goose', 'frog', 'elk', 'deer', 'cat', 'bear', 'ant']

The last example above is the usual way of reversing the order of a list. You simply index the whole list with a step of -1. Make sure that you work through the examples and understand why you get each result. You should also try out other combinations of start, stop and step to consolidate your understanding. You can also use negative indexes for the start and stop parameters. Testing out how this works is left as an exercise for the reader.

Once you get used to Python’s 0-indexing and the fact that stop indices are exclusive, indexing in this was should become second nature12.

1.8.4 Deleting items from lists

As well as adding elements, we may wish to remove element from the list. There are two main ways of doing this. The .pop member function removes an element and allows you to (optionally) store it in another variable. The other option is to re-use the del statement with an index:

# print(my_list)
[10, 20, 30, 40, 50]

# print(thing_removed)
30

# print(my_list) after popping element 2
[10, 20, 40, 50]

# print(my_list) after del'ing element 0
[20, 40, 50]

1.8.5 Sorting and counting items in lists

Another useful operation is to be able to look at the list in a sorted manner. For this, we create a new list which just contains numbers. The sorted function returns a copy of the list which has been sorted. The original list variable remains unchanged.

# print(my_sorted_list)
[20, 30, 40, 50]

# print(my_num_list) - note that the original variable did not change
[50, 40, 30, 20]

If we want to sort the array in-place - i.e. change the current variable, we can use the .sort member function.

[20, 30, 40, 50]

Finally, we have the ability to count the number of specific elements in a list. In the following example, we search for two elements which are in the list and get the counts and then show that we can search for an element which doesn’t exist - in that case, the .count member function returns 0:

# count(20)
2

# count(30)
3

# count(50)
0

In summary, lists are a common data structure in Python. Similar data structures are found in other programming languages; sometimes with other names. Making yourself familiar and comfortable with using lists will make your programming life run a lot more smoothly.

1.8.6 Quick Exercise

Write a script in which you store a set of fifteen scores out of 10 from an experiment (make up the scores and manually put them in a list).

Print out your list, then sort the list and print it again.

Finally, print out the number of people who scored 0, 1, 2, 3… up to 10. (We will discuss how to do this more effectively in the next session using a concept called a loop).

1.9 Collections of Data: Tuples

At first glance, tuples seem to be very similar to lists. In fact, the differences between them are subtle. The main practical difference for new python coders is that once a tuple is set up, you can not change, add or remove items to/from a tuple. This seems to make them fairly useless but they have their uses. For instance, lists cannot be used as keys in a dictionary, whilst tuples can.

We can create an empty tuple using normal round brackets ( ):

# print(type(a))
<class 'tuple'>

# print(a)
()

The problem with creating an empty tuple is that, as mentioned above, they cannot be changed - this therefore means that it will remain empty forever!

More commonly, we would create a tuple with existing content:

(1, 2, 3)

1.9.1 Other tuple operations

Other than being immutable (this is the technical term for being unchangeable), tuples behave like lists. You can extract items and count the length of a tuple in the same way that you can with lists:

# print(my_tuple[1])
10

# print(len(my_tuple))
3

If you try and change an item in a tuple, you will get a TypeError exception:

TypeError                                 Traceback (most recent call last)
      1 my_tuple = (5, 10, 15)
      2
----> 3 my_tuple[0] = 100

TypeError: 'tuple' object does not support item assignment

One final point - if you have a tuple which you need to modify, you can cast it to a list and back again. This does not modify the existing tuple, but creates a new one with new content:

# print(my_orig_tuple)
(1, 2, 3, 4)

# print(my_new_tuple)
(100, 2, 3, 4)

Tuples are so simple that we have not provided any exercises - there is not much which you need to do with them!

1.10 Loops: for

Many, indeed most, programs will want to repeat the same (or similar) operations many times. Examples of this would be making stimuli from a set of parameters or analysing results from multiple participants etc. A loop is the general term for a programming construct which allows us to do this. This week we will consider the for loop - the most common of the loop types which you will encounter.

The general form of a for loop is, as pseudo-code13:

What this means is that LOOPVARIABLE (which can be named whatever you want) will be set to the first item in LIST_OF_THINGS_TO_ITERATE_OVER. The code which is indented (just a comment in this case) will then run. Once all of the code which is indented has been run, the loop will go back to the top, LOOPVARIABLE will be set to the second item in LIST_OF_THINGS_TO_ITERATE_OVER and all of the indented code will run again. This will repeat for each element in LIST_OF_THINGS_TO_ITERATE_OVER.

Be very careful to note the syntax of the loop. The for keyword starts the loop. It is followed by the variable (or, as we will see later, variables) which should be created to run the loop. The keyword in is then used, followed by the expression which gives the list of items over which to iterate. As we will see later, this is not always a list or tuple - there are other possibilities. Finally, there is a comma : which designates that the loop is to start. The code itself is then indented.

One important note is that Python works out how your loop ends by the indentation of the text – i.e. it uses spaces. At first this can be rather confusing and difficult, but it becomes second nature after a while. This does mean that when typing in ipython or in scripts, we have to be very careful to enter things exactly the way we mean to. My general advice is to use four spaces for indenting blocks of code14.

1.10.1 Iterating over a list

In general, a loop is a section of code which repeatedly runs a number of times. In most cases a particular variable will change on each iteration of the loop.

The simplest form of a loop involves setting up a list and then performing some code for each entry in the list. For example, in this case, we print each element in a list followed by itself squared:

# x = 10
10 100
# x = 20
20 400
# x = 30
30 900
# x = 40
40 1600

As a reminder, the comments in the output will not appear on your screen, they are just there to help you follow the output.

Loops are often where people start becoming confused when learning programming. It requires some practice to learn to “visualise” what is happening. As an example, the code above is equivalent to the following written out long-hand:

The output is identical:

10 100
20 400
30 900
40 1600

With only four elements in the list, we need eight lines of code instead of the two which the loop version uses. Now imagine that you are working with thousands of numbers and you can see why loops are so important.

It is important to realise that for loops and other code blocks do not have to be a single line. So, for example, we can write the following:

# x = 10
Hello
10 100
# x = 20
Hello
20 400
# x = 30
Hello
30 900
# x = 40
Hello
40 1600

As long as the code remains indented, the code is part of the block which is in the loop. To clarify, the long-hand version of this code is the following:

The output would be identical. It is worth noting at this point that blank lines in Python code (usually) have no semantic meaning. They can, however, be used to divide up the code for the reader - i.e. they are used for readability for the human, rather than for the computer to care about.

In the main, you can run any code you want inside of a loop. One thing which you should not do however, is to delete an item in a list whilst you are looping over the list. This will cause hard to find problems in your code and almost certainly not do what you want.

1.10.2 Nested loops

We can put for loops inside of each other. This is known as a nested loop. We will first walk through a simple example with two loops fairly carefully. Once you understand the principles, you will be able to deconstruct any example containing any number of nested loops.

It is important to follow the indentation carefully when reading this code. To emphasise what is happening at which points, we have added some extra print calls inside the first, but not second loop.

# x = 1
Before Y loop starts
# y = 50
1 50
# y = 60
1 60
# y = 70
1 70
After Y loop ends
# x = 2
Before Y loop starts
# y = 50
2 50
# y = 60
2 60
# y = 70
2 70
After Y loop ends

As we can see, the first loop uses x as its loop variable. On line 4, this will initially be set to the first value of my_first_list which is 1 (this is represented by x = 1 in the output comments). We then progress to line 5 where we print out the statement Before Y loop starts. On line 7, another loop is constructed. The second loop uses y as its loop variable15. At this point, y will be set to the first value in my_second_list, which is 50 (this is represented by # y = 50 in the output comments). We then execute the code within the loop on line 8 (the print function call) which prints out the values of both x and y. At this point, we run out of code, so we go back to the innermost loop (line 7) and update y to 60. We run the print again, then update y to 70 and run the print again.

We have now finished the “inner” y loop and execute line 10 which prints out the string After Y loop ends. We then go back to line 4 and update the x variable to 2. We now continue to line 5 again, which causes us to go print the Before Y loop starts string and then go around the y loop again, making y equal to 50, 60, and 70 in turn in the same way that we did previously.

The long version is:

The output would be identical to that above, so we do not repeat it here.

Make sure that you understand how nested loops work and how you can control which code happens which number of times by indenting the code appropriately.

1.10.3 range

One common task is to loop over a range of numbers. It would be annoying to have to build up those numbers by hand, so python has a function to help with this - the function is called range. The function returns a generator which will create a range of numbers. This generator is not a list, but can be easily converted into one if needed. In most cases however, you do not have to bother converting and can simply use the generator directly.

The function can be called in several ways:

  • range(stop)

If you do not give a start number, it will be assumed that the numbers should start at 0.

In python, all stop-style parameters are exclusive; i.e. they are not included in the list which is returned. This means that if we ask for range(0, 4), we will get the numbers 0, 1, 2, 3.

# Basic range example - print the generator
<class 'range'>
range(0, 5)

# Basic range example - convert to list and print
<class 'list'>
[0, 1, 2, 3, 4]
  • range(start, stop)

The second way to call range is to provide both the start and stop arguments.

# Example using start and stop
[1, 2, 3, 4]

Finally, the step parameter sets the increment or decrement. Setting this to be negative lets us count backwards.

  • range(start, stop, step)
# range example stepping 2 at a time
[1, 3]

# range example stepping backwards
[5, 4, 3, 2, 1]

# second range example stepping backwards
[4, 3, 2, 1, 0]

You might notice that we are only getting lists of integer numbers from range. If you want to use floating point numbers, you will need to use the arange function from the numpy module – we will cover this in a future session.

1.10.4 Quick Exercise

Write python code, using a loop, to count between -10 and 10 (inclusive) and for each number print out the number and its cube (i.e. the number to the power 3).

1.11 Formatting output

We are often going to want to format output (either to screen or to a file) in a useful and attractive manner. In this section we look at how to print out our data in an ordered and useful way.

To do this, we will use something called f-strings. This is one of three mechanisms for formatting data. In the Appendix, we provide references to the other two older mechanisms: .format and %.

1.11.1 Basic f-strings

The idea behind f-strings is to allow you to insert the values of variables into strings. To do this, we prefix the string with f and we can then use curly braces {} with the variable name inside of it. Our first example:

<class 'str'>
My variable contains the value 100

As we can see from the example, the f-string operator produces a normal string but allows us to use the curly braces in order to insert values. We can do this with as many variables as we want in whichever order we want:

<class 'str'>
My vars are Test, 15.0, 10

You can see from this that the way in which the variable is output depends on the variable type - the float in this case is shown with a .0 on the end whilst the int is not.

We will now investigate how to apply specific formatting to different types of data. The full documentation for format strings can be found at https://docs.python.org/3/library/string.html#formatspec.

1.11.2 Formatting strings

We start by looking at how we can format strings as this will provide us with a basis for looking at the options used with numbers.

Formatting options are always specified with a colon at the end of the variable name and then some numbers or characters.

As our first example, we can tell the code to ensure that our inserted value takes up a certain amount of space by adding, for instance :20 if we want it to take up 20 spaces. We illustrate this by surrounding our value with equals signs. Rather than assigning the result of the f-string interpolation to a variable and then printing it, we skip the variable assignment and just print the result for brevity:

# Print just the variable
===TestText===

# Print the variable taking up 20 spaces
===TestText            ===

Note that in the second example, 20 spaces in total are used. If your inserted string is longer than the space given, it will expand to whatever is needed. We will discuss how to limit this in a moment.

By default, our string is left-centred in the space we give. By adding a character before the number, we can change the alignment:

  • <: left aligned (the default so does not usually need to be specified)
  • >: right aligned
  • ^: centred
# Print the variable left aligned in 20 spaces
===TestText            ===

# Print the variable right aligned in 20 spaces
===            TestText===

# Print the variable centred in 20 spaces
===      TestText      ===

The first example is the same as our previous example, but the second two show how the align characters affect the string which is produced.

As noted above, using the options above, the space will expand to fit the variable, as this example shows:

# Show that the space expands
===TestText===

Note that even though we asked for a 5 character space, it expanded to the 8 characters which are necessary.

If we need to ensure that the space used is only as big as we specify, we have to use something like :8.8 instead. The first number refers to the size of the space and the second number refers to how long the variable is allowed to be within the space. This may seem confusing, but hopefully a few examples will clarify how it works:

# Limit to 6 spaces
===TestTe===

# Limit the variable to 6 spaces in an 8 character space
===TestTe  ===

# Combine the above with right alignment
===  TestTe===

The best way to learn about the formatting characters is to play with variables and experiment for yourself.

1.11.3 Formatting integers

On top of the above options for dealing with strings, we have additional options when it comes to dealing with numbers. We start, however, by experimenting with our basic alignment and spacing options, and note that the default alignment for numbers is to be right-aligned, not left:

# Basic number output - default is right aligned
**   10**

# Left alignment
**10   **

# and centred
** 10  **

The other main option for integers is to zero-pad the number. This means adding 0s before the number to fill the space rather than spaces. As an example, we could convert the integer 5 into 005. This is often useful when naming files to make sure that they sort correctly (e.g. if using participant numbers).

# Basic integer output
**   10**

# Zero padded integer output
**00010**

It is possible to combine zero-padding with left-alignment or centring, but you should generally not do so. The results are likely to be very confusing indeed.

1.11.4 Formatting floating point numbers

Finally in this section, we look at how to format floating point numbers. float formatting has a rather large number of options (as can be seen in the documentation); we will only cover the main ones.

We start with the normal way of printing a floating point number:

# Without any specific formatting control
==10.212==

# Explicitly state that this is a float in formatting
==10.212000==

We see that there is a difference between providing no specification and telling the computer to treat it as a floating point number. In the former case the computer uses a short version of the format whilst in the second case, six places after the decimal point are used as a default.

It is possible to tune the size of the representation of the number by altering both the total number of places in use and the number of them which come after the decimal point:

# Print using 10 spaces in total, of which four are after the dp
==   10.2190==

# Print using 7 spaces in total, of which four are after the dp
==10.2190==

# Print using 7 spaces in total, of which two are after the dp
==  10.22==

The final example above shows that where fewer decimal places are provided than would be needed for the full number, Python will use the default rounding rules (rather than just truncating the number). In most cases this will be the behaviour which you will want.

In addition to this method of printing out floating point numbers, we can instead use scientific notation. This is especially useful when dealing with extremely large or small numbers (e.g. femto-tesla in MEG). We use the e formatting character for this. We can control the total number of spaces and decimal places in the same way as when we used the f formatting character.

# Print using scientific notation with defaults
===1.000000e-06===

# Print using scientific notation with only 3 decimal places
===1.000e-06===

# Print using scientific notation using 10 spaces and 3 decimal places
=== 1.000e-06===

If you have not seen this notation before, you read it as: “1.0 times 10 to the power -6”, i.e. 1.0×106.

As with integers and strings, if the provided number of spaces is not sufficient for the number, extra spaces will be added.

1.11.5 Worked example of formatting

We can now bring together the various techniques we have learned above to put together a line in a table.

Let us assume that we have:

  • A participant ID as an integer which we want to print in the form P001
  • A number of trials (integer)
  • An average reaction time (floating point)

and we want to display it in an ordered way.

We then decide that:

  • The participant ID column will need to be the P plus 3 numbers for the ID. We want the ID zero-padded.
  • The number of trials column should allow us to have up to 9999 trials, so we should make it 4 characters wide as well.
  • The reaction time column should have enough space for something like 1999.222 and that three decimal places is sufficient (that is, after all, millisecond resolution which is more than sufficient). This makes us decide that the column should be 8 characters wide.

We can then work out that our format string (with some characters to delineate the columns) should be:

| P{part_id:03d} | {num_trials:3d} | {rt:8.3f} |

and write it in code for an example line as:

# Print out our table line
| P001 | 100 |  100.210 |

Do not try and remember all of the details of formatting for now, you can refer back to this section later on when we are handling formatted output.

1.11.6 Quick Exercise

Copy and paste the following data into your script:

Write a loop which outputs a formatted table line for each participant. Make sure that you format the participant ID as P001 or similar and that you make sure that the lines in the table all line up.

Remember that you can access any variable inside a format string, include lists and indices; you should be able to write the code for this in two lines.

Your table should look like this:

| P001 | 100 |  100.210 |
| P002 | 150 |  121.542 |
| P003 |  88 |   99.122 |
| P004 | 120 |  145.330 |

1.12 Next session

Next session, we will extend our understanding of data types by looking at a new type called a dictionary. We will also discuss another type of loop called a while loop and look at the other major programming concept - the conditional statement known as if; this allows us to make decisions in our program based on information which we obtain.

Before the tutorial, make sure that you have gone through the exercises for this session as you should be prepared to discuss your answers and ask questions regarding the exercises.

1.13 Take-away exercises

As this is the first week, most of the exercises (except the first one which contains 8 sub-exercises) will only require a small number of lines of code. We have covered all of the necessary techniques this week - you should not need to go searching for any extra information.

When asked to output data, you should format the output neatly using the formatting output options discussed above (where appropriate).

1.13.1 Exercise

Write a single script which prints out the results of the following sums. Place the values into variables before printing them rather than just passing the results to the print function (you will need this for questions g and h).

  1. 1+7
  2. 38
  3. 12
  4. 102
  5. 3+13+2
  6. 212
  7. fb
  8. aba2

1.13.2 Exercise

Write some code which calculates the area of a rectangle using the formula area = width * height

Do this by setting up a variable for width and height, then calculate the area and store it in the area variable. Then print out “Area: x, Height: y, Width: z”.

1.13.3 Exercise

Set up a list called my_list containing the numbers 1 to 4 inclusive. Print the first and last entries in the list. Try and find two different ways to print the last entry in the list.

1.13.4 Exercise

Set up an empty list. Loop around the numbers between 0 and 10 and, on each loop iteration, add the number squared to the end of the list. Print the final list.

1.13.5 Exercise

Set up a tuple containing three floating point numbers: 2.0, 4.0, 6.0. Print out the tuple.

1.13.6 Exercise

Write python code, using a loop, to print every number from 0 to 30 inclusive.

1.13.7 Exercise

Write python code, using a loop, to print every odd number between 0 to 30 inclusive.

1.13.8 Exercise

Write python code to create and output a times table (from 1 to 12) of a number which you define as a variable. Your code should start like this:

# Number for which to produce the times table
number = 3

An example output might start:

 3 x  1 = 3
 3 x  2 = 6
...

1.13.9 Exercise

Take your previous example and use another loop to make it produce multiplication tables for numbers from 1 to 12. Put a title before each table.

Your output should start something like:

Multiplication table for 1

 1 x  1 = 1
 1 x  2 = 2

and end

12 x 11 = 132
12 x 12 = 144

1.13.10 Exercise

Write a program to output a list of temperatures in Celsius and Fahrenheit running from -10oC to 50oC.

The equation for calculating Fahrenheit from Celsius is: f=C95+32.

Print the table neatly.


  1. Not because we hate Word but just because it sneakily adds lots of other characters into the document that you don’t see and this stops your programmes from working.

  2. In 2008 the people who maintain Python decided that the language needed to be upgraded. The upgraded Python was called version ‘3’. This change was going to mean that old Python code (from version 2) would no longer run properly. For about twelve years they kept both versions of Python around : 2 AND 3 and begged people to upgrade their code and programming practises to get ready for the day when version 2 was no longer supported. Needless to say, upgrading lots of old code is the last thing most programmers want to do and when the ‘version 2 sunset date’ rolled around in 2020 there were still lots of people who got cross because they couldn’t run their old programmes safely any more. There is no moral to this story except, perhaps, that changing code always take longer and annoys more people than you expect.

  3. No, not “hash-tag”, that is a specific use of the character; the character is known as the “hash” in English and sometimes Pound (wrongly) in American

  4. Although perhaps the most important thing is that they are written at all. A comment with bad punctuation is usually better than no comment

  5. Many time you will find that person is ‘Future You’. Future You will not remember as many details about the code as you might think and will thank you for commenting it well

  6. You might still see print used without the brackets; e.g. print "Hello World". This is an old syntax and you should get into the habit of including the brackets.

  7. So-called because it allegedly looks like a camel with humps

  8. As long as our computer has enough memory to cope

  9. There are occasions where you will want to do this, but more often you will want to use a function such as math.ceil or math.floor to control the rounding

  10. Handling timezones and dates properly and generally is one of the most fraught areas of coding, even for experienced programmers, so we’ll ignore it for now

  11. Other 0-indexed languages include C, Perl, Ruby and Java. MATLAB on the other hand is 1-indexed - as is Fortran

  12. Some people find it difficult to understand why stop indices are exclusive. It does, however, make sense when working in a 0-indexed language. Consider a list with four items in it: [0, 1, 2, 3] (for ease of discussion we have made the elements be the same as the indices in the list). If you want to extract three items starting at 1, you can do: my_list[1:1+3] or, more generally, my_list[start:start+num_elements]. Although there is not an element 4, Python does what you expect (try it). If the stop indexing was inclusive, you would have add a -1 to your stop index calculation. It also means that len(list) is the correct stop index to use if you want to access the final element; this is less of an advantage in Python as you can always use -1 as a substitution.

  13. Pseudo-code is code which cannot really be run as it is often not syntactically valid. It is often used in explanations to explain the general structure of an idea without needing to use specific code examples

  14. PEP8 https://www.python.org/dev/peps/pep-0008/ contains more justification for this

  15. For (hopefully) obvious reasons, if you have multiple loops, you should never use the same loop variable in more than one loop.