Python program flow control

In this notebook, we will learn how to control the flow of execution of Python code using conditional statements and loops.

Learning objectives for this notebook:

  • Student is able to create conditional branching statements using if, elif, and else
  • Student is able to formulate conditions using ==, <, >
  • Student is able to combine logical conditions using and and or
  • Student is able to create a while loop that exits on a particular condition
  • Student is able to create a for loop that loops over a range of numbers using the range() command
  • Student is able to exit loops using the break command
  • Student is able to skip the rest of a code block in a loop using continue

Conditional statements

A powerful concept in programming languages are pieces of code that allow you to control if segments or your code are executed or not based of the value of variables in your code.

In this section, we will look at some of the features of the Python language for conditional execution of your code.

The if statement

We have already seen an example of the if statement in the previous notebook. The syntax of the if statement is as follows:

if expression:
    code block to be executed if the expression is "True"

Here, expression is a piece of code the evaluates to either the value True or False. Here are a few simple examples:

In [ ]:
if True:
    print("True is true")   
In [ ]:
if True:
    print("The code block")
    print("after 'if' statements")
    print("can have multiple lines.")
In [ ]:
if False:
    print("False is not True (this will not be printed)")
In [ ]:
if not False:
    print("not False is True")   

Exercise 1 Try out the following conditional statements, filling in the values specified to replace the --- in the code cells.

Check if the integer 1 is true:

In [ ]:
if ---:
    print('The expression is true')

Check if 103.51 is true:

In [ ]:
if ---:
    print('The expression is true')

Check if -1 is true:

In [ ]:
if ---:
    print('The expression is true')

Check if condition statements work with boolean variables:

In [ ]:
a = True
if ---:
    print('The expression is true')

Check if conditional statements work with numerical variables:

In [ ]:
b = -73.445
if ---:
    print('The expression is true')

Comparison and test operators

In the above, the expression in the if statements were all directly values that are either True or false (except for the example of not False).

More generally, however, the expression can also include comparisons. Some examples of numerical comparisons are given here below:

In [ ]:
if 5 == 5:
    print("A double equal sign '==' compares the two sides of itself and gives the")
    print("result 'True' if the two sides have the same value")
In [ ]:
a = 5
if a == 5:
    print("== also works with variables")
In [ ]:
a = 5
if 5 == a:
    print("The variable can be on either side")
In [ ]:
a = 5
b = 5
if a == b:
    print("and == also works with two variables")
In [ ]:
a = 5
if a = 5:
    print("This will give an error! A single = is an assignment operator, not a conditional test")
    print("To check if a is equal to 5, you need to use the double equal sign ==")
In [ ]:
a = 5
if a == 6:
    print("This will not be printed.")
In [ ]:
a = 5
if 2*a == 10:
    print("You can also use mathematical expressions")    

In addition to the == operator, there are also several other comparison operators such as <, >, >=, <=, !=

Exercise 2 Test the operators <, >, >=, <=, != by trying them in an if statement with numerical values.

In [ ]:
if --- < ---:
    print('The expression is true')
In [ ]:
if --- <= ---:
    print('The expression is true')
In [ ]:
if --- > ---:
    print('The expression is true')
In [ ]:
if --- >= ---:
    print('The expression is true')
In [ ]:
if --- != ---:
    print('The expression is true')

Logical operations

Python also allows you to build the expression out of logical combinations of several conditions using the keywords and, or, and not. The value of these operators is as follows

  • and evaluates to True if both conditions are True
  • or evaluates to True if either condition is True
  • notevaluates to true if condition is not True

Below are a few examples.

In [ ]:
if True and False:
    print("This will not be printed")
In [ ]:
if True or False:
    print("This will be printed")
In [ ]:
if not 5 > 6:
    print("We have already seen 'not' in previous examples. Here, we also combine with an comparison")
In [ ]:
if not 5 != 5:
    print("This is a very silly example (hard to read, bad coding!)")
In [ ]:
if 5 < 6 and 10 > 9: 
    print("An example of combining conditional expressions with 'and'")

Exercise 3 Try out the following examples using the if statement form from above for the conditions

Check if both 5 is smaller than 6, and 10 is smaller equal than 9

In [ ]:
# Your code here

Check if the statement not (False or True) "is" (i.e. evaluates to) True or False. Compare to not False or True to understand the operation of this condition.

In [ ]:
# Your code here

Check if the statement (not False) or True is True or False

In [ ]:
# Your code here

The elif and else statements

In Python, you can combine the if statement with elif and else commands in a chain in order to allow you to take actions in the case that the starting if statement was false.

elif (else if) is a command that allows you to check another condition if the condition of the starting if is False. Note that if the if criterion is True, the elif statement will be skipped and not checked.

else is a command that allows you to execute some code if all of the if and all the elifs are are False.

Note that to be part of the "chain", all the elifs and the last else must follow directly after each other's code blocks with no other code in between. And a new if always starts a new chain. Obviously, you can also make conditions if an initial condition is fulfllled using nested if-else-elseif statement. In that case you just indent for a second time your secondary condition.

You can see how this works in the following examples:

In [ ]:
a = 5
if a < 6:
    print("a is less than 6")
else:
    print("a is not less than 6")
In [ ]:
a = 5
if a<6: 
    print("the 'if' statement found that a is less than 6")
elif a<6:
    print("this will never get printed!")
In [ ]:
a = 5
if a<6: 
    print("the first if found that a is less than 6")
if a<6:
    print("unlike elif, a second if will get executed.")
In [ ]:
# example of a nested if-statement
a=4
if a<6 and a>=0:
    if a>3:
        print("the value of a is 4 or 5")
    else:
        print("the value of a is 0, 1, 2, or 3")

Here is an example of a function that combines an if, and elif, and an else to check if a number is in a certain range:

Exercise 4 Practice the use of the if-elif-else statement with the following exercise by filling in the missing conditional statements. You must use all three of if, elif and else.

In [ ]:
def check_number(a):
    ...conditional statement number 1...
        print(a, "is less than or equal to 5")
    ...conditional statement number 2...
        print(a, "is between 5 and 10")
    ...conditional statement number 3...
        print(a, "is greater than or equal to 10")
        
# Testing your function
check_number(1)  
check_number(5)  
check_number(7)
check_number(10)
check_number(15)

Loops

Loops are a construction in a programming language that allow you to execute the same piece of code repeatedly.

In Python there are two types of loops: while loops and for loops. We will look at while loops first and then move on to the more common for loops.

The while loop

Using the while command, you can specify that a block of code gets executed over and over again until a certain condition is satified:

while expression:
    code...

As long as expression is true, then the code block will be executed over and over again.

A simple example where we use a while loop to count to 10:

In [ ]:
i = 1
while i <= 10:
    print(i)
    i = i+1

In this example here, we use a while loop to add up all the numbers between 1 and 10:

In [ ]:
i = 1
s = 0

while i<= 10:
    s = s + i
    i = i+1

print("Sum is", s)

Note that with while loops, you have to be careful to make sure that your code block does something that results in your expression becomes false at some point, otherwise the loop will run for ever.

For example, when initially we wrote the above cell to calculate the sum, we forgot the i = i+1 line of code:

In [ ]:
i = 1
s = 0

while i<= 10:
    s = s + i

print("Sum is", s)

This code will never finish: it will go into an infinite loop! (You can see that it is still running because the * beside the In text never gets turned into a number.) Note that for this will have to manually stop the kernel using the stop button in the toolbar or it will run forever...

When should I use a while loop?

For both of the examples above, one would typically use for loop, which we will see in the next section. One place where while loops are very useful is if you do not know ahead of time how many iterations of the loop you will need. For example, let's consider the following sum:

$$ S(n) = \sum_{i=1}^n \sin^2(i) $$

Let's say we want to know: for which $n$ does the sum exceed 30? We can easily check this with a while loop:

In [ ]:
from numpy import sin

i = 1
s = 0

while s<30:
    s += sin(i)**2
    i += 1

print("Sum is", s)
print("i is", i)

Note here I have also used a new operator +=: this is a special assignment operator that increments the variable by the specified amount. a += 1 is equivalent to a = a + 1 (it just saves a bit of typing, remember: programmers are lazy...).

Exercise 5 Write a function factorial(a) to calculate the factorial of a number using a while loop. The factorial, denoted with !, is the product of all numbers up to that number, i.e. $4!=1\cdot2\cdot3\cdot4$. Note that perhaps unexpectedly for you $0!=1$ for which your function also have to give an answer. Make sure, when called, your function returns the factorial value.

In [ ]:
# use notation in line below
# def factorial(a):

###
### Your code here
###


# tests that are performed
print(factorial(4))
print(factorial(0))
In [ ]:
### BEGIN HIDDEN TESTS
question = "answer_1c_5"

to_check = [question + "_%d" % 1]
feedback = ""

key=question + "_%d" % 1

msg = "\nFeedback %s:\n" % key
msg += "="*(len(msg)-2)
msg += "\n\n"

def factorialanswer(a):
    if a!=0:
        i=1
        b=1
        while(i<=a):
            b*=i
            i+=1
    else:
        b=1
    return b

if (factorial(0)==factorialanswer(0)) & (factorial(4)==factorialanswer(4)):
    msg+='Correct answer!'
else:
    msg+='Your answer is incorrect'
   
print(msg); feedback += msg + "n"

assert True==True

### END HIDDEN TESTS

The for loop

The for loop is designed to execute a piece of code a fixed number of times. The syntax of the for loop is:

for i in (a, b, c, d, ...):
    code block

In each subsequent iteration of the for loop, the variable i is assigned next value that is supplied in the list values.

We can repeat our sum calculation above using the following for loop:

In [ ]:
s = 0

for i in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10):
    s += i
    print(s)

Exercise 6 Calculate the sin square sum, stated above for the while loop, for n=10 using a for loop. Name your

In [ ]:
# sumsin2 will be your calculated sum
sumsin2 = 0
    
###
### Your code here
###
In [ ]:
### BEGIN HIDDEN TESTS

question = "answer_1c_6"

to_check = [question + "_%d" % 1]
feedback = ""

key=question + "_%d" % 1

msg = "\nFeedback %s:\n" % key
msg += "="*(len(msg)-2)
msg += "\n\n"

if abs(sumsin2-5.001)<1e-3:
    msg+='Correct answer!'
else:
    msg+='Your answer is incorrect'
print(msg); feedback += msg + "n"

assert True==True

### END HIDDEN TESTS

The range() function

You can imagine that if we wanted to perform this sum up to 100, it would be very annoying to type out all of the numbers. For this, Python has a convenient function range(), than can automatically generate ranges of numbers for you!

The range function can be used in a couple of different ways, which we will look at here. We will see, however, that range does some funny things, which is related to the fact that Python "counts from zero" (more on this later).

But let's look just at some concrete examples for now:

range(N): Print a range of N numbers starting from zero

If you give range only one argument, it will give a range of numbers starting from 0, and a total number of numbers determined by the argument.

As an explicit example, range(10) is equivalent to (0,1,2,3,4,5,6,7,8,9):

In [ ]:
for i in range(10):
    print(i)

range(N,M): Print a range of numbers starting from N and ending at M-1

If you give range two arguments range(N,M), it will give a range of numbers starting from N and stopping at M-1.

In [ ]:
for i in range(1,11):
    print(i)

You might ask: why not stop at M? If you say the words "range from N to M", you would think that this range should include M?

There are two reasons you can think of:

  1. For programmers, it is nice that range(0,10) and range(10) do the same thing
  2. It will be handy later that range(j, j+N) will then always give you N numbers in the range

Maybe there are more, I'm not sure (you'd have to ask the nerds who wrote Python...). But in any case, you just need to remember that range(N,M) stops at M-1...

range(N,M,S): Print a range of numbers less than M, starting from N, with steps of S

This is like the last one, but now with the chance to change the steps:

In [ ]:
for i in range(1,11,2):
    print(i)

Note that the range function works only with integers: range(1,11,0.5) is not allowed. (For floating point ranges, you can use the numpy1 function arange, more on that later...)

Exercise 7 Implement the range function in the code from exercise 6 that you copy here. Check that you've used it properly by making sure you get the same answer if you take range that starts at 1 and ends at 10.

In [ ]:
# Your code here

Using for loops with things other than ranges of numbers

In the examples above, we looked at using for loops to iterate through a list of integers.

In Python, however, for loops are much more flexible than only iterating over numbers: for loops can iterate over any iteratable object, including Python lists and 1-D numpy arrays, both of which we will see later in this lectures.

But, as an example, here is piece of code that uses the numpy random number generator to calculate the sum of 10 random integers between 0 and 100:

In [ ]:
from numpy.random import randint

s = 0
for x in randint(0,100,10):
    print(x)
    s += x

print()
print("Sum is ", s)

Manually exiting or skipping a loop using break and continue

In addition to the examples we saw above, Python offers two extra commands for controlling the flow of execution in a loop: break and continue

break is a command you can use to force the exiting of either a for loop or a while loop. For example, you can replace the while loop above using something like this:

In [ ]:
s = 0
i = 0
while True:
    s += sin(i)**2
    i += 1
    if s > 30: 
        break
        
print(s)

It looks funny at first, because while True looks like you will loop forever! But of course, you are saved by the break statement.

Using break statements can sometimes make your code more readable, particularly if you want to be able to exit the loop under different conditions, or have an exist condition trigger a certain piece of code. Here is an example of two conditions:

In [ ]:
from numpy.random import randint

i = 0
found_five = False
max_tries = 10

while True:
    i += 1
    n = randint(0,30)
    if n == 5:
        found_five = True
        break
    if i >= max_tries: 
        break
        
if found_five:
    print("We found a 5 after", i, "tries")
else:
    print("We didn't find a 5 in the maximum number number of tries (", max_tries, ")")

The statement continue is used if you want to skip the rest of the code in the code block and restart the next loop. This can sometimes be useful if as a way of avoiding adding an else statement. (Remember, programmers are lazy typers...and it can be useful if you have a big long complicated block of code...)

In [ ]:
s = 0
for i in randint(0,30,100):
    if (i % 5) == 0:
        continue
    s += i

print("The sum of 30 random numbers between 0 and 30 excluding those that are divisible by 5 is:", s)   

This is probably not a great example (if you are smart you can do this with less typing), but in any case, you now know what continue does.

Exercise 8 Make a while loop that runs up to a million, but breaks when: the "number" multiplied by "that number minus ten" is equal to 257024. Store the number for which the condition holds in a variable called ystop.

In [ ]:
# name your variable at the last iteration ystop

###
### Your code here
###
In [ ]:
### BEGIN HIDDEN TESTS

question = "answer_1c_8"

to_check = [question + "_%d" % 1]
feedback = ""

key=question + "_%d" % 1

msg = "\nFeedback %s:\n" % key
msg += "="*(len(msg)-2)
msg += "\n\n"

if ystop==512:
    msg+='Correct answer!'
else:
    msg+='Your answer is incorrect'

print(msg); feedback += msg + "n"

assert True==True

### END HIDDEN TESTS