Flow of Control in Python
The programs we’ve written so far are straight-line programs that consist of a sequence of Python statements executed one after the other. The flow of execution is simply a straight sequence of statements, with no branching or looping back to previous statements.
In this chapter, we look at how to change the order in which statements are executed by using if-statements and loops. Both are essential in almost any nontrivial program.
Both if-statements and loops are controlled by logical expressions, and so the first part of this chapter will introduce the idea of Boolean logic.
Read the sample programs in this chapter carefully. Take the time to try them out and make your own modifications.
Boolean Logic
In Python, as in most programming languages, decisions are made using Boolean logic. Boolean logic is all about manipulating so-called truth values, which in Python are written True and False. Boolean logic is simpler than numeric arithmetic, and is a formalization of logical rules you already know.
We combine Boolean values using four main logical operators (or logical connectives): not, and, or, and ==. All decisions that can be made by Python—or any computer language, for that matter—can be made using these logical operators.
Suppose that p and q are two Python variables each labeling Boolean values. Since each has two possible values (True or False), altogether there are four different sets of values for p and q (see the first two columns of Table 4.1). We can now define the logical operators by specifying exactly what value they return for the different truth values of p and q. These kinds of definitions are known as truth tables, and Python uses an internal version of them to evaluate Boolean expressions.
TABLE 4.1 Truth Table for Basic Logical Operators
p |
q |
p == q |
p != q |
p and q |
p or q |
not p |
False |
False |
True |
False |
False |
False |
True |
False |
True |
False |
True |
False |
True |
True |
True |
False |
False |
True |
False |
True |
False |
True |
True |
True |
False |
True |
True |
False |
Logical equivalence
Let’s start with ==. The expression p == q is True only when p and q both have the same truth value—that is, when p and q are either both True or both False. The expression p != q tests if p and q are not the same, and returns True only when they have different values.
>>> False == False True >>> True == False False >>> True == True True >>> False != False False >>> True != False True >>> True != True False
Logical “and”
The Boolean expression p and q is True only when both p is True and q is True. In every other case it is False. The fifth column of Table 4.1 summarizes each case.
>>> False and False False >>> False and True False >>> True and False False >>> True and True True
Logical “or”
The Boolean expression p or q is True exactly when p is True or q is True, or when both are True. This is summarized in the sixth column of Table 4.1. The only slightly tricky case is when both p and q are True. In this case, the expression p or q is True.
>>> False or False False >>> False or True True >>> True or False True >>> True or True True
Logical negation
Finally, the Boolean expression not p is True when p is False, and False when p is True. It essentially flips the value of the variable.
>>> not True False >>> not False True
Evaluating larger Boolean expressions
Since Boolean expressions are used to control both if-statements and loops, it is important to understand how they are evaluated. Just as with arithmetic expressions, Boolean expressions use both brackets and operator precedence to specify the order in which their sub-parts are evaluated.
To evaluate a Boolean expression with brackets
Suppose we want to evaluate the expression not (True and (False or True)). We can do it by following these steps:
not (True and (False or True))
Expressions in brackets are always evaluated first, and so we first evaluate False or True, which is True. This makes the original expression equivalent to this simpler one: not (True and True).
not (True and True)
To evaluate this expression, we again evaluate the expression in brackets first: True and True evaluates to True, which gives us the equivalent expression: not True.
not True
Finally, to evaluate this expression, we simply look up the answer in the last column of Table 4.1: not True evaluates to False. Thus, the entire expression not (True and (False or True)) evaluates to False. You can easily check that this is the correct answer in Python itself:
>>> not (True and (False or True)) False
To evaluate a Boolean expression without brackets
Suppose we want to evaluate the expression not True and False or True. This is the same as the previous one, but this time there are no brackets.
not True and False or True
We first evaluate the operator with the highest precedence, as listed in Table 4.2. In this case, not has the highest precedence, and so not True is evaluated first (the fact that it happens to be at the start of the expression is a coincidence). This simplifies the expression to False and False or True.
TABLE 4.2 Boolean Operator Priority (Highest to Lowest)
p == q
p != q
not p
p and q
p or q
False and False or True
We again evaluate the operator with the highest precedence. According to Table 4.2, and has higher precedence than or, and so False and True is evaluated first. The expression simplifies to False or True.
False or True
This final expression evaluates to True, which is found by looking up the answer in Table 4.1. Thus the original expression, False and not False or True, evaluates to True.
Short-circuit evaluation
The definition of the logical operators given in Table 4.1 is the standard definition you would find in any logic textbook. However, like most modern programming languages, Python uses a simple trick called short-circuit evaluation to speed up the evaluation of some Boolean expressions.
Consider the Boolean expression False and X, where X is any Boolean expression. It turns out that no matter whether X is True or X is False, the entire expression is False. The reason is that the initial False makes the whole and-expression False. The value of False and X does not depend on X—it is always False. In such cases, Python does not evaluate X at all—it simply stops and returns the value False. This can speed up the evaluation of Boolean expressions.
Similarly, Boolean expressions of the form True or X are always True, no matter the value of X. The precise rules for how Python does short-circuiting are given in Table 4.3.
TABLE 4.3 Definition of Boolean Operators in Python
Operation |
Result |
p or q |
if p is False, then q, else p |
p and q |
if p is False, then p, else q |
Most of the time you can ignore short-circuiting and just reap its performance benefits. However, it is useful to remember that Python does this, since every once in a while it could be the source of a subtle bug.