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.
For many beginners, if-statements and loops are two of the trickiest parts of learning how to program. Loops, in particular, can be confusing at first because they require a lot of little pieces of code to work correctly.
So take this chapter slowly and read through the sample programs 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. In Boolean logic, there are only two Boolean values: True and False.
Just as with numbers and strings, you can label particular Boolean values with variables. For instance:
>>> front_sensor_on = True >>> front_sensor_on True
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.
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 just when p and q both have the same true value—that is, when p and q are both True or both False. The expression p != q tests if p and q are not the same, and returns True just when they have different values.
Logical “and”
The Boolean expression p and q is True just 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.
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. It’s important to realize that p or q is true when both p and q are True; Python’s or is sometimes called inclusive-or, because it includes the case when both are 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.
Let’s look at a few examples:
>>> False == False True >>> True and False False >>> False or False False >>> not True False
Python uses an internal version of Table 4.1 to evaluate Boolean expressions.
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 the subparts of the expression are evaluated.
To evaluate a Boolean expression with brackets:
not (True and (False or True))
Expressions in brackets are always evaluated first. Thus, we first evaluate False or True, which is True, and makes the original expression equivalent to this simpler one: not (True and True).
not (True and True)
To evaluate this simpler expression, we again evaluate the expression in brackets first: True and True evaluates to True, which gives us an equivalent, but simpler, 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:
False and not False or True
Suppose the expression you want to evaluate is this one. First evaluate the operator with the highest precedence, as listed in Table 4.2. In the expression False and not False or True, not has the highest precedence, and so not False is evaluated first, which causes the whole expression to simplify to False and True 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 True or True
In this simplified expression from the previous step, we again evaluate the operator with the highest precedence. According to Table 4.2, and has higher precedence than or, and so we evaluate False and True first. Thus 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 technique 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; assume it’s a big, nasty expression that would take you a few seconds to evaluate. It turns out that no matter the value of X, 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. In other words, the value of the expression 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.