6. Decision Making With if, else, and elif

This work is licensed under Creative Commons Attribution-ShareAlike 4.0 International


6.1. Introduction

To move beyond using Python to execute a simple sequence of commands, two new algorithmic features are needed: decision making and repetition (with variation). In this unit we look at decision making, using conditional statements; if statements.

We first see the simplest case of either doing something or not, depending on some condition; here, avoiding division by zero. The code we will use first is:

if b == 0:
    print('Do you really want to try dividing by zero?!')
print(f'{a}/{b} = {a/b}')

Now to test it with various values of a and b.

Initially I will duplicate the code for each test case, but later we will see how to avoid repeating yourself, by either editing the code in the notebook or using commands for repetition.

It is all fine with

a = 4
b = 3
if b == 0:
    print('Do you really want to try dividing by zero?!')
print(f'{a}/{b} = {a/b}')
4/3 = 1.3333333333333333

but not with

a = 4
b = 0
if b == 0:
    print('Do you really want to try dividing by zero?!')
print(f'a/b = {a/b}')
Do you really want to try dividing by zero?!
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-4-87a112b07258> in <module>
      1 if b == 0:
      2     print('Do you really want to try dividing by zero?!')
----> 3 print(f'a/b = {a/b}')

ZeroDivisionError: division by zero

There are several points to note here:

  1. Testing for equality is done with a double equal sign, to distinguish from assigning a value to the variable at the left, which is what b = 0 does.

  2. Statement like the above if ... that control the execution of a subsequent list or block of statements ends with a colon, and statements in the following block are indented by four spaces. Avoid using tabs for indentation! (The JupyterLab and Spyder editors do this indentation automatically when you press “return” after a line of code that ends with a colon.)

  3. The end of the collection of controlled statements in indicated simply by the end of that indentation; unlike some other programming languages, there is no line end, or end if or such. So the second print statement is not in the “if block”, and is executed regardless of what happens in the if statement.

  4. Thus we see that in Python, indentation has meaning; it is not just for readability. (Aside: this and the previous point mimic typical English language style for lists, as indeed in the current cell!)

  5. This code illustrates a classic bit of bad programming: detecting a problem but then not doing anything about it!

On that last point, the next form of conditional statement specifies actions for both the True and False cases, using an else clause for the latter:

a = 4
b = 0
if b == 0:
    print('You cannot divide by zero! Try changing the value of b and rerun this cell.')
else:
    print(f'{a}/{b} = {a/b}')
You cannot divide by zero! Try changing the value of b and rerun this cell.

We can break this into pieces, to see first what b == 0 does:

answer = (b == 0)
print(f"The answer is {answer}")
The answer is True

Note that logical statements like b == 0 have value either True or False — and remember that these are case sensitive; “true” and “false” have no special meaning.

if answer:
    print('You cannot divide by zero! Try changing the value of b and rerunning this cell.')
else:
    print(f'{a}/{b} = {a/b}')
You cannot divide by zero! Try changing the value of b and rerunning this cell.

Next, exercise the other “else” option.

Aside: it is good code development practice to use a collection of tests which ensure that every piece of code is executed.

a = 4
b = 3
answer = (b == 0)
print(f"The answer is {answer}")
The answer is False
if answer:
    print('You cannot divide by zero! Try changing the value of b and rerun this cell.')
else:
    print(f'{a}/{b} = {a/b}')
4/3 = 1.3333333333333333

Aside: avoiding code duplication, with the for statement

We will learn about commands for repetition in a later unit, but just as a preview, here is an example of how to do a succession of similar things without duplicating lines of code.

Also note the two levels of indentation, each with four spaces.

a = 4
for b in [3, 0]:
    print(f'With a = {a} and b = {b},')
    if b == 0:
        print('b is 0; you cannot divide by zero!')
    else:
        print(f'{a}/{b} = {a/b}')
With a = 4 and b = 3,
4/3 = 1.3333333333333333
With a = 4 and b = 0,
b is 0; you cannot divide by zero!

Here is another way to do the same thing, this time putting the more “normal” case first, which tends to improve readability:

a = 4
b = 3
if b != 0:  # Note: This is the notation for "is not equal to".
    print(f'{a}/{b} = {a/b}')
else:
    print('b is 0; you cannot divide by zero!')
4/3 = 1.3333333333333333

6.2. Handling multiple possibilities

More than two possibilities can be handled, using an elif clause (short for “else, if”). Let’s see this, while also introducing inequality comparisons:

x = 4
if x > 0:
    print('x is positive')
elif x < 0:
    print('x is negative')
else:  # By process of elimination ...
    print('x is zero')
x is positive

6.2.1. Exercise A

Test the above by changing to various other values of x and rerunning. As mentioned above, ensure that you exercise all three possibilities.

While experimenting in a notebook (or Python code file), one way to do this is to edit in new values for x and then re-run, but for final presentation and submission of your work, do this by one of the methods seen above:

  • make multiple copies of the code, one for each chosen value of x.

  • be more adventurous by working out how to use a for statement to run a list of test cases. This is looking ahead to Iteration with for.

6.3. There can be as many elif clauses as you want.

In the following, remember that a % b is the remainder after integer division of a by b:

n = 36
if n % 10 == 0:
    print(f'{n} is a multiple of ten')
elif n % 5 == 0:
    print(f'{n} is an odd multiple of five')
elif n % 2 == 0:
    print(f'{n} is even, but not a multiple of five.')
else:
    print(f'{n} has no factor of either two or five.')
36 is even, but not a multiple of five.

6.3.1. Exercise B

Again, test all four possibilities by using a suitable collection of values for n.

6.4. Plan before you code!

Start with written preparation and planning of your procedure, and check that with me before creating any Python code.

You can do this on paper or a text file, but as a goal for how best to do things, you could also try to present this planing in a Jupyter notebook; then you could append the Python work to that notebook later.

As part of this planning, select a collection of test cases that explore all the possibilities: keep in mind the goal that each line of code is executed in a least one test case (bearing in mind that if statements can skip some lines of code, depending on whether various statements are true or false).

6.4.1. Exercise C. Planning for robust handling of all possible “quadratics” \(ax^2 + bx + c = 0\)

In the next exercise, we will create Python code that correctly handles the task of finding all the real roots of a quadratic equation \(ax^2 + bx + c = 0\) for the input of any real numbers \(a\), \(b\) and \(c\).

Before writing any code, we need a written plan, distinguishing all qualitatively different cases for the input, and deciding how to handle each of them. (This planning and decision making will be an important requirement in many later exercises.)

Produce quadratic solving code that is robust; handling all possible triples of real numbers a, b and c for the coefficients in equation \(ax^2 + bx + c = 0\).

Not all choices of those coefficients will give two distinct real roots, so work out all the possibilities, and try to handle them all.