8.4. NB3: Program flow control with Conditional Statements and Loops#

  • Learning objectives

  • Conditional statements

    • The if statement

    • Comparison and test operators

    • Logical operations

    • The elif and else statements

  • Loops

    • The while loop

    • When should I use a while loop?

    • The for loop

    • The range() function

    • Using for loops with things other than ranges of numbers

    • Manually exiting or skipping a loop using break and continue

  • Solutions to Exercises

8.4.1. Pre/Post-test#

This test is for testing your current skills in Python. You can use it in two ways:

  • pre-test: to test your skills beforehand. If you are already proficient in Python, and can do this test within approximately 15 minutes, you can scan through the notebook rather than carefully reading each sentence.

  • post-test: to test your skills after Notebook 3. Check whether you learned enough.

8.4.1.1. Including password protection#

Although the supervisors are impressed by the work Eric is doing, his co-workers are not as happy by the quick progress Eric is making. Due to the large number of error messages in his code recently, he is convinced someone is messing with his code… Therefore he decides to write a program that asks the user to enter the password and checks whether it is correct. As a typo might occur, the user has three tries, after which the program should shut down. After every try tell the user whether the password is correct or not and how many tries are left. If the user has not provided the right passwork within the required number of attempts, provide a warning message.

Write the code.

password = 'practicum123'
tries = 0 

### Your code

8.4.2. Learning objectives#

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

After completing this notebook, you are able to:

  • create conditional branching statements using if, elif, and else

  • formulate conditions using ==, <, >

  • combine logical conditions using and and or

  • create a while loop that exits on a particular condition

  • create a for loop that loops over a range of numbers using the range() command

  • exit loops using the break command

  • skip the rest of a code block in a loop using continue

8.4.3. 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.

from IPython.lib.display import YouTubeVideo
YouTubeVideo('DZwmZ8Usvnk', width = 200, height = 150)

8.4.3.1. 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 that evaluates to either the value True or False. Here are a few simple examples:

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

Exercise 3.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:

if ---:
    print('The expression is true')
  Cell In[7], line 1
    if ---:
          ^
SyntaxError: invalid syntax

Check if 103.51 is true:

if ---:
    print('The expression is true')

Check if -1 is true:

if ---:
    print('The expression is true')

Check if condition statements work with boolean variables:

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

Check if conditional statements work with numerical variables:

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

8.4.3.2. 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:

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")
a = 5
if a == 5:
    print("== also works with variables")
a = 5
if 5 == a:
    print("The variable can be on either side")
a = 5
b = 5
if a == b:
    print("and == also works with two variables")
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 ==")
a = 5
if a == 6:
    print("This will not be printed.")
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 3.2
Test the operators <, >, >=, <=, != by trying them in an if statement with numerical values.

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

8.4.3.3. 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.

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

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

(a) Check if 5 is smaller than 6, AND 10 is smaller equal than 9:

# Your code here

(b) Do you think that the statement not False or True evaluates to True or False? Try it out and see:

# Your code here

To understand what happened in part (b), we have to know if Python first performs the operation False or True or if it performs the operation not False first. The rules for which order Python does things, in can be found in the documentation for operator precedence. In the example above, we can see that the not operator had precedence and Python performed the not before it performed the or.

What if I wanted to have Python perform the or first? You do this by enclosing True  or False in brackets:

if not (False or True):
    print("not (False or True) is False so this will not be printed")

8.4.3.4. 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. You can see how this works in the following examples:

a = 5
if a < 6:
    print("a is less than 6")
else:
    print("a is not less than 6")
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!")
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.")

Since the code inside your if code block is just regular code, you can also add another if statement inside that code block. This creates a nested if statement inside your first one:

# 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")
else:
    print("none of these are the case")

Exercise 3.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. You also can not use the end operator.

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)

8.4.4. 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.

8.4.4.1. 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:

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:

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 forever.

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

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…

8.4.4.2. 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:

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 3.5
Write a function 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.

If your code doesn’t seem to stop running, you may need to “debug” your code to see where your mistake is (I did). A handy way to do this, for example, is to add a print(i) statement inside your while loop: it will enable you to see if the variable i is doing what you think it should be…

def factorial(a):
    ...
    while(...)
    
print(factorial(4))
print(factorial(0))

8.4.4.3. 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:

s = 0

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

It would, however, be much more convenient if we would not have to type all the numbers. Our could be something like this:

s = 0
i = np.arange(1,11)
for i in i:
    s+=i
    print(s)

And that brings us to the range function…

Exercise 3.6
Calculate the sum \(\sum_{i=0}^n\sin^2(i)\) for n=10 using a for loop.

# Your code here
for...:

8.4.4.4. 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:

8.4.4.5. 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):

for i in range(10):
    print(i)

8.4.4.6. 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.

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 I 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

8.4.4.7. 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:

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 numpy function arange, more on that later…)

Exercise 3.7
Calculate the sum of all numbers from 1 to 100 using a for loop with the range() function. Compare it to the famous value that Gauss calculated in class as an elementary school student.

# Your code here

8.4.4.8. 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 1-D numpy arrays, which we will see in later notebooks.

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

from numpy.random import randint

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

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

8.4.4.9. 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:

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 exit condition trigger a certain piece of code. Here is an example of two conditions:

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…)

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

print("The sum of +/-100 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 3.8
Write code that creates a variable i with an initial value of zero. Use a while loop that increments i by one as long as i is less than one million. Have your loop stop if i satisfies the condition \(i(i-10) = 257024\). Have your code print the i that satisfies this condition (if there is one).

# Your code here

Exercise 3.9
In order to make sure that the seasons stay aligned with our calendars, leap years of 366 days have been introduced. A leap year is a year which is an integer multiple of 4 (except for years evenly divisible by 100, but not by 400), https://en.wikipedia.org/wiki/Leap_year.

Make a function to check whether a certain year is a leap year using if/else statements to return True for leap years and False for normal years.

def leap_year(year):
    
    return
# Check if your function works. The years 4, 2000, 2012 are examples of leap years,
# whereas the years 2021 and 2100 are not.
years = [1, 4, 100, 400, 2000, 2012, 2020, 2021, 2024, 2100]
for year in years:
    print(year, leap_year(year))
#autotestingscript
import numpy as np
test = []

for year in years:
    test.append(leap_year(year)) 

answercheck = [False, True, False, True, True, True, True, False, True, False]
#auto check
np.testing.assert_array_equal(test, answercheck, 'error', True)

Exercise 3.10
Sometimes we want to find the peaks in our data. One way to do this is to go through the array and check for every value if the values left and right are smaller.

Write some code that finds the peak(s) of the array and that prints the index and value of all peaks.

y = np.array([2, 2, 3, 4, 5, 4, 3, 8, 6, 4])    # dummy data

# your code here

Exercise 3.11
To keep your computer safe from others, it is probably password protected. Usually you have a few tries before the password entry is disabled for some time.

You can enter your password three times before a timeout is given. If the timeout (sleep) is over, you have three other tries. The timeout time in seconds is given by \(T = 60 + i^3\), where \(i\) is the number of tries.

Write the code. 3pt

#your code here

password = 'practicum123'
tries = 0 

8.4.4.10. Solutions to exercises#

Exercise 3.1

if 1:
    print('The expression is true')
if 103.51:
    print('The expression is true')
if -1:
    print('The expression is true')
a = True
if a:
    print('The expression is true')
b = -73.445
if b:
    print('The expression is true')

Exercise 3.2

if 5 < 5.1:
    print('The expression is true')
if 5 <= 5:
    print('The expression is true')
if 5.1 > 5.1:
    print('The expression is true')
if 5.1 >= 5.1:
    print('The expression is true')
# "!=" is "not-equals"
if 5 != 5.1:
    print('The expression is true')

Exercise 3.3 (a)

if 5 < 6 and 10 <= 9:
    print("i don't think this is true")

(b)

if not False or True:
    print("it is true?")
else:
    print("it wasn't true?")

Exercise 3.4

def check_number(a):
    if a <= 5:
        print(a, "is less than or equal to 5")
    elif a < 10: 
        # note we don't need to test if it's greater than 5 since the first if already took 
        # care of that for us
        print(a, "is between 5 and 10")
    else:
        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)

Exercise 3.5

def factorial(a):
    # f = our factorial result
    f = 1
    i = 2
    while(i<=a):
        f *= i
        i += 1
    return f
    
print(factorial(4))
print(factorial(2))

Exercise 3.6

# Your code here

s = 0
for i in range(11):
    s += np.sin(i)**2

print("Sum is", s)

Exercise 3.7

s = 0

# Note the range should end at 101!
# It should also start at 1: range(101) will also execute 
# the loop with i=0 (which won't matter here...)
for i in range(1,101):
    s += i

print(s)

Exercise 3.8

# Solution

maxnum=1e6
i=0
while i <= maxnum:
    if i*(i-10)==257024:
        break
    i+=1
    
print(i)

Exercise 3.9

def leap_year(year):
    if year % 4 == 0:
        if year % 100 == 0:
            if year % 400 == 0:
                return True
            return False
        return True
    return False

Exercise 3.10

## Solution
import numpy as np
y = np.array([2, 2, 3, 4, 5, 4, 3, 8, 6, 4]) 

for i in range(1, len(y)-1):
    if y[i-1] < y[i] and y[i+1] < y[i]:
        print('index:', i, '\t value:', y[i])                                  

Exercise 3.11

password = 'practicum123'
tries = 0 

from time import sleep

while True:
    input_pw = input('What is the password?')
    if input_pw == password:
        break
    if (tries+1)%3==0:
        sleep(60+tries**3)
    tries += 1