If Statements (Selection)

Introduction to Software Engineering (CSSE 1001)

Author

Paul Vrbik

Published

March 13, 2025

Logical Operations and Constants

The following are the basic operands of logic.

  1. or,
  2. and,
  3. not.

which are functions into the bool type. Functions that return True or False, such as these, are called predicates.

For instance, greater than is a predicate which takes two numbers and returns True when the first larger than the second.

7 > 3 
True

And (Conjunctions)

The binary predicate and is used to test that both of two predicate statements are True.

An expressions with and is True only when both of its inputs are True.

False and False
False
False and True
False
True and False
False
True and True
True

Or (Disjunctions)

The binary predicate or is used to test that at least one of two predicate statements is True.

An expressions with or is False only when both of its inputs are False.

False or False
False
False or True
True
True or False
True
True or True
True

Examples

3>7 or 7>3  
True
3>7 and 7>3  
False
0 < 3 and 3 < 8
True
Tip

There is a nice shorthand for statements like 0 < 3 and 3 < 8.

0 < 3 < 8
True

Associativity

Consider that

True or False and False

is ambiguous because left-bracketing gives

(True or False) and False 
False

but right-bracketing gives

True or (False and False) == True
True

and thus an order of operations is necessary to resolve ambiguities.

Observe

True or False and False
True

and thereby and has precedence over or.

Exercise 1 Bracket the following boolean expression so that it evaluates to False.

False and False or True and False or False or True

How many different bracketings can you find?

Not

The logical statement not is the negation of logical statement:

not(False)
True
not(True)
False

Examples

a, b = 6, 7  
a == 6  
True
a == 6 and b == 5  
False
not(a == 7 and b == 5)  
True
a != 7 and not b != 7  
True

Exercise 2 Add a single not to

False and False or True and False or False or True

so that it evaluates to False.

Short Circuits / Lazy Computation

Python is a lazy programming language which means it does not perform computation unless it absolutely must. The consequence of this is that expressions may not get executed when and-ing and or-ing them.

For instance, we do not throw a division by zero error in the following because Python does not both evaluating 1/0 because the outcome of that expressions has no bearing on the outcome (True or ANYTHING is True).

True or 1/0 
True

We do however, throw it here because Python needed to check if 1/0 is True because the first input was False.

False or 1/0 
ZeroDivisionError: division by zero

Examples

The following is fine because Python never gets around to checking if non_existent_variable has a value.

True or non_existent_variable 
True

The following it not fine because it is not in Python’s language – it does not satisfy the syntax rules.

True or ! 
  True or !
          ^
SyntaxError: invalid syntax

Python needs to check if 1/0 is truthy to determine the outcome of the following. This is the point at which the error occurs.

True and 1/0 
ZeroDivisionError: division by zero
False and 1/0 
False

Keep in mind that the result of comparison operators can be stored in memory.

a = 3 
a == 3
a != 3
False
b = (a != 3) 
b
False

Exercise 3 Determine what outputs.

a = True + 2  
a

True has an integer representation as 1.

3
b = 7*False  
b

False has an integer representation as 0

0
c = not a != b  
c

The outcome of comparisons (indeed of any operation) can be stored in variables.

False

Truthiness

Values in Python that bool converts to True are called truthy.

Example 1 Non-zero numbers, non-empty strings, and non-empty lists are all truthy.

bool(1), bool(-10), bool("Hello"), bool([1,2,3])
(True, True, True, True)

Conversely, values in Python that bool converts to True are called falsy.

Example 2 Zero, the empty string, and the empty list are all falsy.

bool(0), bool(""), bool([])
(False, False, False)

Exercise 8 Which of the following expressions evaluate to True?

bool(not [] and "hello")  
True
bool(True or 1/0)  
True
bool(" ")
True
bool(1 > False)  
True

The “truth” about And and Or

The functions and and or do not always return True and False as we may expect. In Python, and returns its first falsy input and or its first truthy input. In the case no such value exists, the last input is returned.

0 or 2
2
0 or 0
0
[] or [1] or [1, 2]
[1]
[1, 2] or [1] or []
[1, 2]
0 and 2
0
1 and 2
2
[] and [1] and [1, 2]
[]
[1, 2] and [1] and []
[]

The if statement.

Given a condition \(C\) (i.e. a predicate that evaluates to True or False) an if-statement is a control structure that executes a block of code when \(P\) is True (and skips it otherwise).

Figure 1: Flow-chart for if-statement. Diamonds denote decisions and rectangles arbitrary pieces of code.

If-Then

if <cond>:
␣␣␣␣<code executed when cond == True>

In Python spaces matter — only code indented within an if-statement gets executed.

x = 0
if False:
    x = x + 1
x 
0
x = 0 
if True:
    x = x +1
x 
1
x = 0
if x:
    x = x + 1
x 
0
x = 1 
if x:
    x = x +1
x 
2
def foo(x):
    if x > 0:
        print("Positive")
    if x > 10**5:
        print("Large positive")

ans = foo(10**6) 
Positive
Large positive
type(ans) 
NoneType
def bar(x):
    if x > 0:
        return "Positive"
    if x > 10**5:
        return "Large positive"

ans = bar(10**6) 
type(ans) 
str
ans 
'Positive'
Warning

Take care when defining variables inside if-statements.

if False:
    ans = 0
ans
NameError: name `ans' is not defined

The code in the if-statement is skipped and therefore ans does not get set.

In this case it is best to define a default value for ans.

Consider that writing

if balance >= 0:
    in_the_black = True
    in_the_red = False

if balance < 0:
    in_the_black = False
    int_the_red = True

makes our code longer and checks the condition twice.

If-Then-Else

Figure 2: Flow-chart for else-if-statement. Notice we can now branch.

Here is the general framework for working with if-then statements.

if <cond>:
    <code>
else:
    <code>
if balance >= 0:
    in_the_black = True
    in_the_red = False
else: 
    in_the_black = False
    int_the_red = True

if-else does not allow you to skip code, but rather lets us pick between two instruction sets.

Else-If

Figure 3: Flow-chart for else-if-statement. Notice there are multiple diamonds / decisions here (in particular three, but we could have as many as we like).

Here is the general framework for working with if-elif statements.

age = 60

if age >= 18:
    beverage = "cheap beer"
elif age >= 30:
    beverage = "standard beer"
elif age >= 50:
    beverage = "expensive beer"

beverage 
'cheap beer'

This code does not work as intended!

Fix 1

We could specify lower and upper bounds on the age .

age = 60

if 18 <= age < 30:
    beverage = "cheap beer"
elif 30 <= age < 50:
    beverage = "standard beer"
elif 50 <= age:
    beverage = "expensive beer"

beverage 
'expensive beer'

Notice for this fix we did not need elif statements because the following is equivalent to what we just wrote.

age = 60

if 18 <= age < 30:
    beverage = "cheap beer"

if 30 <= age < 50:
    beverage = "standard beer"

if 50 <= age:
    beverage = "expensive beer"

beverage 
'expensive beer'

Fix 2

If we revese the way we check for age and re-introduce the elif we can simplify the code somewhat.

age = 60

if age >= 50:
    beverage = "expensive beer"
elif age >= 30:  # Guaranteed `age < 50`
    beverage = "standard beer"
elif age >= 18:  # Guaranteed `age < 50 and age < 30`
    beverage = "cheap"

beverage 
'expensive beer'

If-Elif-Else

Figure 4: Flow-chart for else-eliif-else-statement. Notice there is now a catch for when none of the conditions are satisfied.

Here is the general framework for working with if-elif-else statements.

if <cond0>:
    <code>
elif <cond1>:
    <code>
    .
    .
    .
elif <condN>:
    <code>
else:
    <code>

Exercise 7 What will beverage evaluate to after this code is run?

age = 9

if age >= 50:
    beverage = "expensive beer"
elif age >= 30:
    beverage = "standard beer"
elif age >= 18:
    beverage = "cheap"

beverage 
NameError: name `beverage' is not defined

To fix this, we add a else branch to account for the extra case.

age = 9

if age >= 50:
    beverage = "expensive beer"
elif age >= 30:
    beverage = "standard beer"
elif age >= 18:
    beverage = "cheap"
else:
    beverage = "apple juice"

beverage 
'apple juice'

Refactoring

In computer science factoring means breaking a complex problem into parts that are easier to conceive, understand, program, and maintain.

Code refactoring is the process of restructuring existing computer code – changing the factoring – without changing its behavior.

Here are a list of common scenarios where refactoring could be done.

Example 3 Nested if-statements should be avoided by using and because

if x>1:
    if y>2:
        if z>3:
            print("hello")

is equivalent to

if x>1 and y>2 and z>3:
    print("hello")

Example 4 No need for an if-statement in the following code because

def foo(x):
    if x > 0:
        return True
    else:
        return False

is equivalent to

def foo(x):
    return x > 0

Example 5 No need for an if-statement in the following code because

if x > 0:
    y = True
else:
    y = False

is equivalent to

y = x > 0

Example 6 No need to equality check on True because

if x > y == True:
    z = 1

is equivalent to

if x > y:
    z = 1

Example 7 No need to equality check on False because

if x > y == False:
    z = 1

is equivalent to

if not x > y:
    z = 1

Exercise 4 Are the following pieces of code equivalent?

#Block 1
if x > 0:
    print("A")
elif x <= 0 and x % 2 == 0:
    print("B")
if x > 0:
    print("A")
elif x % 2 == 0:
    print("B")

Yes!

if x > 0:
    print("A")
elif x <= 0 and x % 2 == 0:
    print("B")

is equivalent to

if x > 0:
    print("A")
elif x % 2 == 0:  # because here it must be that x <= 0
    print("B")

Exercise 5 Are the following pieces of code equivalent?

if x > 0:
    print("A")
if x <= 0 and x % 2 == 0:
    print("B")
if x > 0:
    print("A")
if x % 2 == 0:
    print("B")

No!

if x > 0:
    print("A")
if x <= 0 and x % 2 == 0:
    print("B")

is not equivalent to

if x > 0:
    print("A")
if x % 2 == 0:  # this if is independent of the one before it
    print("B")

Let x = 2. The first if prints A and the second prints A and B.

Exercise 6 Refactor the code.

def foo(x, y):
    if x > 100 and y > 0:
        if y > 100 and x > 0:
            return "A"
        elif y > 100 or x > 0:
            return "A"
        else:
            return "B"
    elif y <= 0 or x <= 0:
        if x == y:
            return "A"
        if x <= y and x >= y:
            return "B"
        if y < x and x < y:
            return "C"
        else:
            return "A"
    else:
        if x <= 100 and y > 0:
            return "A"
        if x > 100 or y <= 0:
            return "B"
        else:
            return "D"
def foo(x, y):
    return "A"

Common Errors

Or True

The following will always print.

if x == 1 or 2 or 3:
    print("hello")

It is not equivalent to

if x == 1 or x == 2 or x == 3:
    print("hello")

which we will eventually refactor as

if x in [1,2,3]:
    print("hello")

This is because

x == 1 or 2 or 3

is evaluated like

(x == 1) or (2) or (3)

which is equivalent to

(x == 1) or True or True

which is always True.

Empty If-Branches

The following is not an error per-se, but is common student code and should not be done.

if x:
    pass
else
    print("Hello World")

is equivalent to

if not x:
    print("Hello World")

Summary

Blocks of code can be skipped using if statements. This control flow depends on the evaluation of predicate statements.

Next Iteration!