Controlling program flow

Up to now, the short scripts we’ve written have been simple linear sequences of commands we want Python to execute.

While some simple tasks won’t go beyond this format, most scripts and programs use conditional execution and looping to achieve their tasks.

Conditional execution: if...else

Many computational tasks require us to conditionally perform some operations, or where the computation forks depending on the value of some variable.

For example, we might have a calculation that at some point generates a distance.

We then want to print the distance in a nice human readable format; if the distance is 0.0001 m we might prefer it to read “0.1 mm”, while if the distance is 32000 m printing “20 miles” would be more useful.

As with most programming languages, this conditional execution is achieved through use of an if-else statement.

For example,

# Somewhere in a chunk of code, we have calculated a distance in meters
# which we store in variable "dist" 
print(dist)                          # This would print 0.0001 or 32000 in the two cases above
if (dist < 0.1):
    print("Which is " + str( dist * 1000 ) + " mm")
elif( dist > 1609 ):
    print("Which is " + str( dist / 1609 ) + " miles")
else:
    print("Which is " + str(dist) + " meters")

Here we’ve shown the three possible parts of an if block; the if part will always be present, while the elif and else are optional.

The syntax is:

if <STATEMENT THAT EVALUATES TO TRUE OR FALSE> : 
    # Stuff to do if it's true

elif <ANOTHER STATEMENT THAT EVALUATES TO TRUE OR FALSE>:     # OPTIONAL 
    # Stuff to do if the `if` part was False but this part is True
 
else: # OPTIONAL 
    # Stuff to do if all previous if/elifs were False. 

Reminder: Indentation!

As mentioned at the end of the first section, the indentation above is not accidental!

Python uses indentation to demarcate logical blocks. In this case, everything after the if statement that is indented is inside the if block.

Returning to the previous level of indentation signals the end of the if block.

The elif and else are the only exceptions in this case, as they are logical extensions to the if block.

To further illustrate this consider the following code;

if a < 5:
    print("Ya")
print("Hoo")

This section of code will always print Hoo (irrespective of the value contained in a) and only print Ya if a is less than 5.

On the other hand

if a < 5:
    print("Ya") 
    print("Hoo")

will only print Ya followed by Hoo (if a is less than 5) or nothing (if a is more than or equal to 5).

Changing just the indentation level of the second print statement, has determined whether it is part of the if block or not.

Other languages

If you’ve learnt C,C++, or Java, then indentation is roughly the same as curly brackets in those languages. For example the last snippet of code in Java would be written as

if( a<5 ){
    System.out.println("Ya");
    System.out.println("Hoo");
}

(where in Java the indentation is optional!)

Looping with for-in

To really start to benefit from having a computer do things for us, we often need it to do almost the same thing repeatedly, with only small changes between each time it repeats.

For example, we might want to batch process a directory full of data files, in which case we change only the filename between each run and the program treats each file in the same way.

We might be iterating over a set of time series and applying the same filters and analyses to each one.

Or on an even finer-scale level, most image processing algorithms work by, at some point, looping over all the pixels in an image and performing the same operation at each location.

Whatever the level of processing, these scenarios all have one thing in common; they’re achieved by using loops. In most cases, we would choose a for loop, which is a way of iterating over a set list of elements (files, line-series, pixels). In most languages, we have to explicitly change the base object of each iteration, usually by indexing into a list.

This can also be done with Python. For example

l = ["a","b","c","d","e"]
for i in range(len(l)):
    print(l[i])

outputs:

a
b
c
d
e

Here,

  • we start with a list of 5 letters
  • Then we start a for loop over the list of index values generated by range, 0-4
  • Inside the loop, we print the value of the list at the index value

i is usually referred to as the loop variable as it’s the thing that changes between each invocation of the code inside the loop.

So in each iteration of the loop, i takes on a value (from 0 to 4) and we use that value to index the original list l, and print the item in the list (letter) at index i.

However, in Python, instead of having to loop over indices, we can loop over the items we’re interested in themselves directly;

l = ["a","b","c","d","e"]
for c in l:
    print(c)

outputs:

a
b
c
d
e

While we haven’t saved any lines of code (yet!), the syntax has become much more readable.

Loosely translated:

for c in l: is the same as for each item in l that we’ll call c.

As shown above, we can still use indecies if we prefer.

Auto-generating the index variable with enumerate

Python even provides a convenience function, called enumerate, that returns both an index and a value for each item in a sequence:

l = ["a","b","c","d","e"]
for i,c in enumerate(l):
    print("At index ", i, "the item is ", c)

outputs:

At index  0 the item is  a
At index  1 the item is  b
At index  2 the item is  c
At index  3 the item is  d
At index  4 the item is  e

Exercise : Conditional Flow & Loops - number sorting

Write a script (name the file exercise_loops.py) that creates a list of the numbers 0 to 49 inclusive (0, 1, .., 49), and then prints out for each one whether it is odd or even.

Another loop construct: while

There is another looping mechanism we can use instead of the for loop, called a while loop. while loops are mainly used when the exact number of times we want to repeat something is not known before starting the loop. For things like pixels and files in a directory, we would usually use for loops as we know before starting the loop how many files there are or how many pixels there are. But if we had an optimization algorithm, such algorithms usually continue until a desired closeness to an ideal solution has been found (or a maximum number of tries has been reached).

As writing optimization algorithms is difficult, we’re going to simulate one using the random number module instead.

For example

import random # This imports the random number module 
n = 1
tries = 0
while n > 0.1:
    n = random.random()     # This generates a random number between 0 and 1
    tries += 1
print("It took", tries, "tries to roll a random number less than 0.1, =", n)

A typical output from this would be

It took 3 tries to roll a random number less than 0.1, = 0.07209438256934753

So what’s happening here?

After importing the random module (more on this in the next section!), we start with two variables, n for the random number, and tries to hold the number of times we’ve tried.

We set n to 1 so that the while loop will be executed at least once (otherwise if e.g. n starts at 0, then the while condition will immediately be False and the loop would never be entered!), and tries to 0.

Then, inside the while loop, we generate a new value for the random number, and increase tries by 1.

As we then come to the end of the body of the while block, the condition gets tested again - is n still more than 0.1 or not? If so, go again. And so on until n is less than (or equal to) 0.1. Once n is less than or equal to 0.1, the comparison n > 0.1 returns False, and the while loop stops.

Then we proceed to the next line and print the text and values.

Can you guess what would happen if we removed the line that generates a new random value for n?

If you’re not sure, work through the snippet line by line and see if you can figure it out.

If you’re still not sure, ask a demonstrator.

Exercise : Conditional while loops

Write a script (name the file exercise_while.py) that determines how many squares (startng with the sequence 12 = 1, 22 = 4, 32 = 9, etc) are needed, such that their sum exceeds 1,000,000.

Have the script print out the number of squares needed and their sum (which should exceed 1,000,000!).