4.3. Flow Control#

4.3.1. Conditional Statements and Blocks#

Sometimes, we will only want to execute some piece of code if a certain condition is met.

These conditions can be anything.

For example, we might add to total sales if the transaction value is positive, but add to total returns if the value is negative.

Or, we might want to add up all incurred costs, only if the transaction happened before a certain date.

We use conditionals to run particular pieces of code when certain criterion are met.

Conditionals are closely tied to booleans, so if you don’t remember what those are, go back to the basics lecture for a refresher.

The basic syntax for conditionals is

if condition:
    # code to run when condition is True
else:
    # code to run if no conditions above are True

Note that immediately following the condition, there is a colon and that the next line begins with blank spaces.

Using 4 spaces is a very strong convention, so that is what we do — we recommend that you do the same.

Also note that the else clause is optional.

Let’s see some simple examples.

if True:
    print("This is where `True` code is run")
This is where `True` code is run

Alternatively, you could have a test which returns a booleans

if 1 < 2:
     print("This is where `True` code is run")
This is where `True` code is run

This example is equivalent to just typing the print statement, but the example below isn’t…

if False:
    print("This is where `True` code is run")

Or

if 1 > 2:
     print("This is where `True` code is run")

Notice that when you run the cells above nothing is printed.

That is because the condition for the if statement was not true, so the code inside the indented block was never run.

This also allows us to demonstrate the role of indentation in determining the “block” of code.

val = False

if val is True: # check an expression
    print("This is where `True` code is run")
    print("More code in the if block")
print("Code runs after 'if' block, regardless of val")
Code runs after 'if' block, regardless of val

The next example shows us how else works.

val = (2 == 4)  # returns False
if val is True:
    print("This is where `True` code is run")
else:
    print("This is where `False` code is run")
    print("More else code")
print("Code runs after 'if' block, regardless of val")
This is where `False` code is run
More else code
Code runs after 'if' block, regardless of val

The if False: ... part of this example is the same as the example before, but now, we added an else: clause.

In this case, because the conditional for the if statement was not True, the if code block was not executed, but the else block was.

Finally, the Condition is True is assumed in the if statement, and is often left out. For example, the following are identical

if (1 < 2) is True:
    print("1 < 2")

if 1 < 2:
    print("1 < 2")
1 < 2
1 < 2

4.3.1.1. elif clauses#

Sometimes, you have more than one condition you want to check.

For example, you might want to run a different set of code based on which quarter a particular transaction took place in.

In this case you could check whether the date is in Q1, or in Q2, or in Q3, or if not any of these it must be in Q4.

The way to express this type of conditional is to use one or more elif clause in addition to the if and the else.

The syntax is

if condition1:
    # code to run when condition1 is True
elif condition2:
    # code to run when condition2 is True
elif condition3:
    # code to run when condition3 is True
else:
    # code to run when none of the above are true

You can include as many elif clauses as you want.

As before, the else part is optional.

Here’s how we might express the quarter example referred to above.

import datetime
halloween = datetime.date(2017, 10, 31)

if halloween.month > 9:
    print("Halloween is in Q4")
elif halloween.month > 6:
    print("Halloween is in Q3")
elif halloween.month > 3:
    print("Halloween is in Q2")
else:
    print("Halloween is in Q1")
Halloween is in Q4

Note that when there are multiple if or elif conditions, only the code corresponding to the first true clause is run.

We saw this in action above.

We know that when halloween.month > 9 is true, then halloween.month > 6 and halloween.month > 3 must also be true, but only the code block associated with halloween.month > 9 was printed.

4.3.2. Iteration#

When doing computations or analyzing data, we often need to repeat certain operations a finite number of times or until some condition is met.

Examples include processing all data files in a directory (folder), aggregating revenues and costs for every period in a year, or computing the net present value of certain assets. (In fact, later in this section, we will verify the equations that we wrote down above.)

These are all examples of a programming concept called iteration.

We feel the concept is best understood through example, so we will present a contrived example and then discuss the details behind doing iteration in Python.

4.3.2.1. A Contrived Example#

Suppose we wanted to print out the first 10 integers and their squares.

We could do something like this.

print(f"1**2 = {1**2}")
print(f"2**2 = {2**2}")
print(f"3**2 = {3**2}")
print(f"4**2 = {4**2}")
# .. and so on until 10
1**2 = 1
2**2 = 4
3**2 = 9
4**2 = 16

As you can see, the code above is repetitive.

For each integer, the code is exactly the same except for the two places where the “current” integer appears.

Suppose that I asked you to write the same print statement for an int stored in a variable named i.

You might write the following code:

print(f"{i}**2 = {i**2}")

This more general version of the operation suggests a strategy for achieving our goal with less repetition: have a variable i take on the values 1 through 10 (Quiz: How can we use range to create the numbers 1 to 10?) and run the line of code above for each new value of i.

This can be accomplished with a for loop!

for i in range(1, 11):
     print(f"{i}**2 = {i**2}")
1**2 = 1
2**2 = 4
3**2 = 9
4**2 = 16
5**2 = 25
6**2 = 36
7**2 = 49
8**2 = 64
9**2 = 81
10**2 = 100

Whoa, what just happened?

The integer i took on the values in range(1, 11) one by one and for each new value it did the operations in the indented block (here just one line that called the print function).

4.3.2.2. for Loops#

The general structure of a standard for loop is as follows.

for item in iterable:
   # operation 1 with item
   # operation 2 with item
   # ...
   # operation N with item

where iterable is anything capable of producing one item at a time (see here for official definition from the Python team).

We’ve actually already seen some of the most common iterables!

Lists, tuples, dicts, and range/zip/enumerate objects are all iterables.

Note that we can have as many operations as we want inside the indented block.

We will refer to the indented block as the “body” of the loop.

When the for loop is executed, item will take on one value from iterable at a time and execute the loop body for each value.

When iterating, each item in iterable might actually contain more than one value.

4.3.2.3. while Loops#

A related but slightly different form of iteration is to repeat something until some condition is met.

This is typically achieved using a while loop.

The structure of a while loop is

while True_condition:
    # repeat these steps

where True_condition is some conditional statement that should evaluate to True when iterations should continue and False when Python should stop iterating.

For example, suppose we wanted to know the smallest N such that \( \sum_{i=0}^N i > 1000 \).

We figure this out using a while loop as follows.

total = 0
i = 0
while total <= 1000:
    i = i + 1
    total = total + i

print("The answer is", i)
The answer is 45

Let’s check our work.

# Should be just less than 1000 because range(45) goes from 0 to 44
sum(range(45))
990
# should be between 990 + 45 = 1035
sum(range(46))
1035

A warning: one common programming error with while loops is to forget to set the variable you use in the condition prior to executing. For example, take the following code which correctly sets a counter

i = 0

And then executes a while loop

while i < 3:
    print(i)
    i = i + 1
print("done")
0
1
2
done

No problems. But if you were to execute the above cell again, or another cell, the i=3 remains, and code is never executed (since i < 3 begins as False).

while i < 3:
    print(i)
    i = i + 1
print("done")
done

4.3.2.4. break and continue#

4.3.2.4.1. break Out of a Loop#

Sometimes we want to stop a loop early if some condition is met.

Let’s revisit the example of finding the smallest N such that \( \sum_{i=0}^N i > 1000 \).

Clearly N must be less than 1000, so we know we will find the answer if we start with a for loop over all items in range(1001).

Then, we can keep a running total as we proceed and tell Python to stop iterating through our range once total goes above 1000.

total = 0
for i in range(1001):
    total = total + i
    if total > 1000:
        break

print("The answer is", i)
The answer is 45

4.3.2.4.2. continue to the Next Iteration#

Sometimes we might want to stop the body of a loop early if a condition is met.

To do this we can use the continue keyword.

The basic syntax for doing this is:

for item in iterable:
    # always do these operations
    if condition:
        continue

    # only do these operations if condition is False

Inside the loop body, Python will stop that loop iteration of the loop and continue directly to the next iteration when it encounters the continue statement.

For example, suppose I ask you to loop over the numbers 1 to 10 and print out the message “{i} An odd number!” whenever the number i is odd, and do nothing otherwise.

You can use continue to do this as follows:

for i in range(1, 11):
    if i % 2 == 0:  # an even number... This is modulus division
        continue

    print(i, "is an odd number!")
1 is an odd number!
3 is an odd number!
5 is an odd number!
7 is an odd number!
9 is an odd number!