2.0 Controlling Python’s Flow

When programmers discuss flow control, they refer to the way the execution of code is directed based on certain conditions. Python is an interpreted language, meaning it executes code line by line from the top down. That means the order of written code is incredibly important to the program’s behavior. Python provides several flow statements However, it also provides mechanisms to alter this linear flow based on conditions, allowing for more complex and dynamic behavior in programs.

Boolean Operations

Booleans are True and False, which is fairly straightforward, but they are actually a lot more than that. Remember from chapter 1, we indicated that variables are always stored as their value after evaluation. For instance, x = 5 + 5 stores x in memory as 10, not as the expression 5 + 5. The same is true for boolean expressions. When you write a boolean expression, Python evaluates it and stores the result as either True or False. If we take the expression 5 < 10, Python evaluates it and stores the result as True. This is how Python handles boolean operations internally, and it allows us to use these expressions in conditional statements to control the flow of our programs.

Boolean operations are fundamental in programming, allowing you to make decisions based on conditions. In Python, boolean operations are used to evaluate expressions that result in either True or False. These operations help us control the flow of your program, enabling you to execute different code paths based on specific conditions.

Comparisons

Comparison operators are used to compare two values and return a boolean result (True or False). We have already introduced some comparisons above. The most common comparison operators in Python are:

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

Comparison operators are often used in conditional statements to control the flow of execution based on the results of these comparisons.

x = 5
y = 10

print(f"x < y : {x < y}")  # True
print(f"x == y : {x == y}")  # False
print(f"x != y : {x != y}")  # True
x < y : True
x == y : False
x != y : True
Note

Notice the f-string syntax used in the print statements. This allows for easy embedding of expressions within strings (using the curly brackets {} for the variable), making it clear what is being printed. See chap. 1 for more details on f-strings. In fact, you can write complete python expressions inside the curly brackets, and Python will evaluate them before printing.

But we can also use comparisons beyond mathematical comparisons. We can check, for instance, if a string is equal to another string, or if a list has enough or too many values. Really, the options are endless. Take for example the following code. Let’s say we have a program that tracks a person’s name, age, and habits. We can use comparisons to check if the name is “Alice”, if the age is greater than 25, and if the number of habits exceeds a maximum limit.

name = "Alice"
age = 25
habits = ["reading", "hiking", "coding"]
maximum_habits = 5

print(f"name == 'Alice' ? {name == 'Alice'}")  # True
print(f"age > 25 ? {age > 25}")  # False
print(f"Does {name} have too many habits? : {len(habits) > maximum_habits}")  # False
name == 'Alice' ? True
age > 25 ? False
Does Alice have too many habits? : False

Here we set the name to “Alice”, the age to 25, and the maximum number of habits with a variable maximum_habits to 5. name == 'Alice' evaluates to True, age > 25 evaluates to False because age is not greater than 25. Then we check if the length of the habits list exceeds that maximum using the len() function, which returns the number of items in the list. The result is False, indicating that Alice does not have too many habits.

We can also use more advanced math as comparisons as well (See Chap. 1.3 for more on advanced math notations). A very common example is checking if a number is odd or even. We can do this by checking if the modulus (%, which returns the remainder of a division operation) of 2 is equal to 0, which would indicate that it is even. If it is not equal to 0, then it is odd.

number = 7
is_even = number % 2 == 0
print(f"Is {number} even? {is_even}")  # False    
Is 7 even? False

The in Operator

The in operator is used to check if a value exists within an iterable, such as a list, tuple, or string. It returns True if the value is found and False otherwise. This operator is particularly useful for simplifying membership tests.

fruits = ["apple", "banana", "cherry"]
if "banana" in fruits:
    print("Banana is in the list!")

We can use it on any collection, such as a list, tuple, or even a string. For example, if we have a list of numbers and we want to check if a specific number is in that list, we can do the following:

numbers = [1, 2, 3, 4, 5]
print(f"Is 3 in the numbers list? {3 in numbers}")
Is 3 in the numbers list? True

Similarly, we can use it to check if a character is in a string:

text = "Hello, world!"
print(f"H is in the text? {'H' in text}")
H is in the text? True

Finally, we can use it to check if a key exists in a dictionary:

user = {"name": "Alice", "age": 30}
print(f"Name is a key in the user dictionary: {'name' in user}")
Name is a key in the user dictionary: True

And, Or, Not

Boolean operations can be combined using logical operators such as and, or, and not. These operators allow you to create more complex conditions by combining multiple boolean expressions.

And

The and operator is used to combine two or more boolean expressions. It returns True only if all the expressions are True. If any expression is False, the entire expression evaluates to False. In addition, if the first expression is False, Python will not evaluate the second expression, which can be useful for performance optimization.

x = 5
y = 10
z = 15

# Using 'and'
print("Is x less than y and y less than z?", x < y and y < z)  # True
print("Is x greater than y and y greater than z?", x > y and y > z)  # False
Is x less than y and y less than z? True
Is x greater than y and y greater than z? False
Note

Here we used a comma to separate the two conditions within the print() function rather than using an f-string. This is a valid way to print multiple values in Python, but it does not allow for string interpolation like f-strings do.

Notice here that the and operator allows us to combine multiple conditions. In this case, both x < y and y < z must be True for the entire expression to be True. If either condition is False, the result will be False.

Or

The or operator is used to combine two or more boolean expressions. It returns True if at least one of the expressions is True. If all expressions are False, the entire expression evaluates to False. If the first expression is True, Python will not evaluate the second expression, which can also be useful for performance optimization.

# Using 'or'
x = 5
y = 10
z = 15
print(f"Is at least one condition is True? {x < y or y > z}")  # True
print(f"Is both conditions are True? {x > y or y < z}")  # True
print(f"Are both conditions False? {x > y or y > z}")  # False
Is at least one condition is True? True
Is both conditions are True? True
Are both conditions False? False

The or operator will always read both conditions, but it will only evaluate the second condition if the first one is False. This can be useful for short-circuiting evaluations, where you want to avoid unnecessary computations if the first condition is already True.

Not

The not operator is used to negate a boolean expression. It returns True if the expression is False, and False if the expression is True. This operator is useful for reversing the logic of a condition.

# Using 'not'
x = 10
y = 15
z = 20
print("x is not greater than y.", not (x > y))  # True
print("y is not less than z.", not (y < z))  # False
x is not greater than y. True
y is not less than z. False

The not operator negates the result of the expression inside the parentheses. If the expression is True, not will make it False, and vice versa. This can be useful for creating conditions that check for the opposite of a given condition. In other words, x is not greater than y, so it should evaluate to False, but we add the not operator to reverse the logic (not False) converting it to True.

We could use the not operator differently as well. For instance, we can use it to check if a value is not in a collection by combining it with the in operator. This is useful when we want to ensure that a certain value does not exist in a list, set, or dictionary:

fruits = ["apple", "banana", "cherry"]

print("Is 'orange' not in the fruits list?", not ("orange" in fruits))  # True
Is 'orange' not in the fruits list? True