Conditionals

In the previous tutorial, we mentioned – without detailing – the notion of conditional statements, through the example of membership tests. Now, we will delve into the details of how conditions work in Python. They are a major step in creating programs that automate operations since they allow you to execute or not execute code based on certain conditions. They enable the computer to make decisions based on criteria set by the user.

The boolean type

In its simplest form, a test in Python is an expression that evaluates to “true” or “false.” For example, the expression \(3 > 2\) is true, so the associated test will return “true.” For this type of evaluation, Python has a particular type of object: booleans. Unlike the object types we have seen (int, float, str, etc.), booleans can only take two values: True and False.

type(True)
bool

Like any object, booleans can be assigned to variables.

a = False
print(a)
print(type(a))
False
<class 'bool'>

The values True and False must be written precisely this way (first letter capitalized, no quotes). They also cannot be used as variable names to avoid ambiguities.

a = true  # Python will look for the variable `true`, but it doesn't exist
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[51], line 1
----> 1 a = true  # Python will look for the variable `true`, but it doesn't exist

NameError: name 'true' is not defined
True = 3
  Cell In[52], line 1
    True = 3
    ^
SyntaxError: cannot assign to True

Comparison operators

Comparison operators formalize mathematical comparison operations (equality, inequality, inequalities). They compare two values and return a boolean value.

Operator | Meaning |
| |
== | Equal to |
!= | Not equal to |
< | Less than |
> | Greater than |
<= | Less than or equal to |
>= | Greater than or equal to |

Let’s illustrate these operators with a few examples.

3 == 3
True
63 == 36
False
2 != 3
True
2 != 2
False
3 > 2
True
a = 36
a <= a
True
a < a
False

Everything seems to work correctly for usual mathematical operations. But these operators actually work on any type of object.

'do re mi fa sol' == 'do re mi fa sol'
True
'do re mi fa sol' == 'Do Re Mi Fa Sol'
False
'duck' != 'bee'
True
True == True
True
True == False
False
[1, 2, 3] == [1, 2, 3]
True
[1, 2] != [3, 4]
True

Finally, it is possible to chain comparisons. The expression returns True provided that each of the comparisons is true.

5 < 7 <= 8
True
5 < 7 <= 6
False

Boolean operators

Boolean operators allow simultaneous testing of multiple logical expressions. Fundamentally, these operators take two boolean values as input and return a single boolean value according to fixed logic rules. These rules are stated in truth tables.

and operator

The first boolean operator is and. Let’s look at its truth table:

Expression | Evaluation |
| |
True and True | True |
True and False | False |
False and True | False |
False and False | False |

Let’s verify these rules in practice with a few examples.

True and True
True
False and True
False

The rules seem to work on boolean values. Of course, in practice, we are more interested in evaluating real logical expressions. We can use these operators to test expressions that return a boolean value.

(3 > 2) and (5 <= 9)
True
a = ("x" != "z")
b = ("x" == "y")
a and b
False

Note the use of parentheses to delimit the tests: they are not mandatory but are strongly recommended as they greatly improve the readability of tests.

or operator

The second boolean operator is or. Its truth table is as follows:

Expression | Evaluation |
| |
True or True | True |
True or False | True |
False or True | True |
False or False | False |
True or True
True
False or True
True
(3 > 2) or (5 <= 9)
True
a = ("x" != "z")
b = ("x" == "y")
a or b
True

not operator

The last boolean operator is not. Its truth table is as follows:

Expression Evaluation
not True False
not False True
not True
False
not False
True
not (3 + 3 == 6)
False
not (7 < 7)
True

Conditional structures

All the expressions we have seen so far are boolean expressions: a test is performed, and the operation returns True or False depending on whether the evaluated expression is true or not. In the context of a computer program that automates operations, we want to use them as conditions: if the expression is true, then the computer must perform a certain operation. Conditional structures allow precisely this usage.

Let’s illustrate this principle by implementing the following program:

  • Define a variable x.

  • If x is greater than \(5\), then print “The expression is true.”

  • Otherwise, print “The expression is false.”

Vary the value of x to verify the correct operation of the test.

x = 7

if x >= 5:
    print("The expression is true.")
else:
    print("The expression is false.")
The expression is true.

Instruction blocks and indentation

The previous example illustrates the syntax of conditional structures in Python. These structures are based on instruction blocks, which delimit the set of instructions to be executed when a test is true. Conditional structures have three rules:

  • The line specifying the test ends with :.

  • All instructions that must be executed if the test is true are at the same level of indentation.

  • The conditional structure ends when the indentation returns to its original level.

Note that conditional structures can indeed be nested, as illustrated in the following example.

x = 7

if x >= 5:
    print("The first expression is true.")
    if x >= 12:
        print("The second expression is true.")
The first expression is true.

When x = 7, the first test returns True, so the instruction block at indentation level 1 is executed line by line. The second test returns False, so the instruction block at indentation level 2 is not executed.

Vary the value of x so that both blocks are executed.

if, else, and elif statements

In conditional structures, tests can be specified using three statements: if, else, and elif. The previous examples have already illustrated the operation of the first two (and most frequent) statements.

In the case of a simple test (a single condition), only an if statement will be used, whose operation is simple: if the condition (test) returns True, then the instruction block (indented) that follows is executed. If the condition returns False, nothing happens. Let’s illustrate this with a membership test, for which we have seen examples in the previous tutorial.

client = "Isidore"

if client in ["Alexandrine", "Achille", "Colette"]:
    print("Known client.")

In practice, we often want to specify an alternative when the condition of the if statement returns False. The else statement allows specifying an alternative instruction block.

client = "Isidore"

if client in ["Alexandrine", "Achille", "Colette"]:
    print("Known client.")
else:
    print("Unknown client.")
Unknown client.

Finally, we may want to specify multiple alternatives. In this case, we will use elif statements. The first elif statement will only execute if the test of the if statement returns False. The second elif statement will only execute if the test of the first elif statement returns False, and so on. Again, a final else statement can be specified, which only executes if none of the previous tests returned True.

client = "Isidore"

if client == "Alexandrine":
    print("Hello Alexandrine.")
elif client == "Achille":
    print("Hello Achille.")
elif client == "Colette":
    print("Hello Colette.")
else:
    print("Hello dear unknown.")
Hello dear unknown.

NB: The previous instructions are for example purposes only. In practice, there are much more concise ways to code a program that performs the same operations.

Exercises

Comprehension questions

  • 1/ What is the particularity of booleans compared to other basic object types in Python?

  • 2/ What are the inputs and outputs of a comparison operator?

  • 3/ What types of objects can be compared using a comparison operator?

  • 4/ What is the difference between the = operator and the == operator?

  • 5/ What are the inputs and outputs of a boolean operator?

  • 6/ Explain in English the principle of the and boolean operator. Same for or and not.

  • 7/ What is the difference between a boolean expression and a condition?

  • 8/ What is the structure of a conditional statement?

  • 9/ Can conditional statements be nested?

  • 10/ Among the if, else, and elif statements, which ones are mandatory and which ones are optional?

Show solution

1/ They have only two values: True and False. Other types have an infinite number of possible values.

2/ Inputs: two values. Output: boolean value.

3/ All types of objects. In practice, however, it often does not make much sense to compare objects of different types; the result will generally be False.

4/ The = operator assigns a value to a variable. The == operator tests the equality of two objects.

5/ Inputs: two boolean values or two expressions that return booleans. Output: boolean value.

6/ The and operator returns True if both of its inputs are True, and False in all other cases. The or operator returns True if at least one of its inputs is True, and False if both are False. The not operator returns False if its input is True, and True otherwise.

7/ In both cases, they are tests. A condition is when the expressions are used within conditional structures.

8/ The conditional statement starts with an if, else, or elif statement, ending with :. Then comes an indented instruction block that only executes if the statement is True. The block ends when the indentation returns to its original level.

9/ Yes, conditional statements can be nested indefinitely (in theory). Just be careful to respect the levels of indentation.

10/ Only the if statement is mandatory.

Predicting test results

Predict the results of the following tests and verify your predictions:

  • 'Simon' in ['simon', 'oceane', 'veronique']

  • [1, 2, 3] == ['1', '2', '3']

  • 'x' != 'x'

  • (9 > 5) and (3 == 5)

  • (3 > 2 and 5 >= 1) or (5 <= 9 and 6 > 12)

  • not (9 > 2*3)

  • not (9 > (2*3))

  • not ((7 > 8) or (5 <= 5))

  • (True and True) or (True == False)

  • (not False) or (not True)

# Test your answer in this cell
Show solution
  • 'Simon' in ['simon', 'oceane', 'veronique'] : False

  • [1, 2, 3] == ['1', '2', '3'] : False

  • 'x' != 'x' : False

  • (9 > 5) and (3 == 5) : False

  • (3 > 2 and 5 >= 1) or (5 <= 9 and 6 > 12) : True

  • not (9 > 2*3) : False

  • not (9 > (2*3)) : False

  • not ((7 > 8) or (5 <= 5)) : False

  • (True and True) or (True == False) : True

  • (not False) or (not True) : True

Predicting nested test results

Consider the program written in the following cell.

x = 10

if True:
    print("Initialization.")
    l = []
    if x > 8:
        l.append("a")
    elif x >= 2:
        l.append("b")
    else:
        l.append("c")
    if x - 6 < 0:
        print("Negative.")
        
print(l)
Initialization.
['a']

For the following values:

  • x = 1

  • x = 5

  • x = 10

predict the program’s results:

  • What is l at the end of the program?

  • What is printed in the console during the program?

Verify your results.

# Test your answer in this cell
Show solution
  • x = 1 : l = ['c'] and messages printed: ‘Initialization’ and ‘Negative’

  • x = 5 : l = ['b'] and messages printed: ‘Initialization’ and ‘Negative’

  • x = 10 : l = ['a'] and messages printed: ‘Initialization’

Three and no more

Write a program that performs the following operations:

  • Define a list that contains 4 names.

  • Write a test that prints the message (‘Too many people.’) if the list contains more than three people.

  • Remove a person from the list (using the del function or the pop method seen in a previous tutorial).

  • Perform the test again, there should be no output.

# Test your answer in this cell
Show solution
people = ["Romuald", "Ursula", "Jean-Vincent", "Philomène"]

if len(people) > 3:
    print('Too many people.')

print(people)    
people.remove("Jean-Vincent")
print(people)

if len(people) > 3:
    print('Too many people.')
Too many people.
['Romuald', 'Ursula', 'Jean-Vincent', 'Philomène']
['Romuald', 'Ursula', 'Philomène']

The price is right

The input function allows the user to enter a value in a Python program. The syntax is as follows: x = input(). When this command is executed, the user must enter a value, which is then assigned to the variable x.

Using input and the if, elif, and else statements, code the following program:

  • Ask the user for a value, which will be stored in a variable p.

  • If p is strictly less than $15, print the message “too low!”.

  • If p is strictly greater than $15, print the message “too high!”.

  • If p is equal to $15, print the message “right on!”.

Note, input returns a string by default. Therefore, you need to convert the value of p to an integer (using the int function) for the game to work.

# Test your answer in this cell
Show solution
p = input()
p = int(p)

if p < 15:
    print("too low!")
elif p > 15:
    print("too high!")
else:
    print("right on!")
---------------------------------------------------------------------------
StdinNotImplementedError                  Traceback (most recent call last)
Cell In[92], line 1
----> 1 p = input()
      2 p = int(p)
      4 if p < 15:

File /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/ipykernel/kernelbase.py:1281, in Kernel.raw_input(self, prompt)
   1279 if not self._allow_stdin:
   1280     msg = "raw_input was called, but this frontend does not support input requests."
-> 1281     raise StdinNotImplementedError(msg)
   1282 return self._input_request(
   1283     str(prompt),
   1284     self._parent_ident["shell"],
   1285     self.get_parent("shell"),
   1286     password=False,
   1287 )

StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.

Boolean evaluation of various objects

In Python, all objects evaluate to True or False in a conditional test (if/else). The general rule is that objects that are zero or empty (e.g., an empty list, an empty dictionary) evaluate to False, and vice versa. But there is no need to know these rules by heart: they can be easily found in practice! For example, you can use the following conditional test:

if "test":
    print("True.")
else:
    print("False.")
True.

Predict which boolean value the following objects will evaluate to, and verify using the previous syntax.

  • 0

  • 1

  • 12

  • -1

  • ’’ (empty string)

  • ’ ’ (string containing only a space)

  • [] (empty list)

  • [''] (list containing only an empty string)

  • {}

  • {-1}

# Test your answer in this cell
Show solution
  • 0 : False

  • 1 : True

  • 12 : True

  • -1: True

  • '' (empty string): False

  • ' ' (string containing only a space): True

  • [] (empty list): False

  • [''] (list containing only an empty string): True

  • {}: False

  • {-1}: True

Chained comparisons

We have seen that it is possible to perform chained comparisons, which return True provided that each of the included comparisons is true. Find a way to rewrite the following chained comparison using boolean operators.

5 < 7 <= 8 < 18

# Test your answer in this cell
Show solution
print(5 < 7 <= 8 < 18)

print(5 < 7 and 7 <= 8 and 8 < 18)
True
True

A chained comparison can be rewritten with and operators. It makes sense: each comparison must be true for the whole to be true. In practice, the version with and is probably preferable for readability.

Booleans and binary language

Booleans are strongly linked to binary language, where `1

corresponds to "true" and0` to “false.” Let’s see if this link exists in the context of Python. To do this:

  • Calculate the “integer representation” of the boolean value of your choice using the int function.

  • Use booleans in the context of mathematical calculations to verify their behavior in this context.

# Test your answer in this cell
Show solution
print(int(True))  
1

A boolean evaluated as an integer indeed gives the associated binary value.

print(True + 3)  
4
Booleans behave like their associated integer value in calculations.

String comparisons

What do inequality comparison tests applied to strings return? Produce some examples to test the behavior.

# Test your answer in this cell
Show solution
print("a" > "b")
print("a" < "b")
print("apricot" > "avocado")
print("apricot" < "avocado")
print("1" > "2")
print("1" < "2")
print("A1" < "A2")
False
True
False
True
False
True
True

The order relation used is alphanumeric order: each character is taken individually, and the orders are A < Z and 1 < 9.

Equality tests on floats

Equality tests between real numbers (type float in Python) can be misleading. To convince yourself, perform the following test: (6 - 5.8) == 0.2

To understand the test result, perform the calculation of the left-hand side of the test alone. What do you notice?

Imagine (without necessarily implementing it) another test, based on inequalities, that would allow testing approximate equality.

# Test your answer in this cell
Show solution
diff = 3 - 2.7

print(diff == 0.3)

print(diff)
False
0.2999999999999998

In Python, floating-point numbers are always approximate values. We can therefore have such surprises in calculations.

tolerance = 0.0001
new_test = (0.3 - tolerance) < diff < (0.3 + tolerance)
print(new_test)
True
This last test allows testing the equality between diff and 0.3 approximately, allowing for some tolerance in the comparison.