Logic#

You may remember that in our first lab we came across boolean data:

type(True)
bool
type(False)
bool

True and False are reserved keywords that represent logical values.

Logical operators#

Back in lab 1, we became familiar with the basic mathematical operators: +, -, *, /, ** etc. Python also provides us with a suite of logical operators that evaluate whether a given expression is True or False.

The simplest example is the == operator:

5 == 5
True
5 == 6
False

This operator tests whether or not the values on either side are equal to one another. The examples above are somewhat trivial, but we could use the same syntax with variables:

a = 5
b = 6

c = a * b

c == 30
True

Warning

It is very easy to confuse the “equals to” operator == with the assignment operator =, but they serve very different purposes. The = operator assigns the value or the right hand side to the variable name on the left, whilst the == operator is comparing the values on either side to see whether or not they are equal to one another.

In short, a = 2 means “I am telling you that a is equal to 2”, whereas a == 2 means “Is a equal to 2?”

Rather than testing whether or not two values are equal to one another, we can also test the exact opposite: whether or not two values are not equal to one another:

5 != 6
True
5 != 5
False

A set of these comparitive operators can be found in the table below.

Condition

Mathematical Notation

Operator

Equals

\(=\)

==

Not equal to

\(\neq\)

!=

Less than

\(<\)

<

Less than or equal to

\(\le\)

<=

Greater than

\(>\)

>

Greater than or equal to

\(\ge\)

>=

5 < 6
True
5 > 6
False

Just like the basic mathematical operations, these logical operators can cause errors if the types of the input data don’t make any sense:

5 <= 'ten'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[10], line 1
----> 1 5 <= 'ten'

TypeError: '<=' not supported between instances of 'int' and 'str'

Error

Here we get a TypeError because we are comparing an int and a str - we cannot compare the numerical value of an integer and a string!

In addition to the comparitive operators we have discussed so far, Python also allows us to test whether or not a given element can be found in a sequence:

'Fe' in ['Cr', 'Mn', 'Fe', 'Co', 'Ni']
True

The example above uses a list, but we can also search for letters in a string:

'r' in 'Iron'
True
'f' in 'Iron'
False

To perform the opposite test, we can use not in to see if a given element is not in a sequence:

'r' not in 'Iron'
False
'f' not in 'Iron'
True

and and or#

We now have the toolkit to evaluate basic logical statements:

favourite_colour = 'purple' # Assign 'purple' to favourite_colour

favourite_colour == 'red' # Evaluate whether or not favourite_colour equals 'red'
False

Notice the comments, this example highlights nicely the difference between the = and == operators.

What if we want to combine logical operators? For example, what if we want to know whether a number is greater than 4, but less than 10? We already know how to test these two conditions separately:

number = 11

print(number > 4)
print(number < 10)
True
False

In this example, \(11\) is greater than \(4\), so the first expression evaluates to True. \(11\) is also greater than \(10\) though, so the second expression evaluates to False. To combine these two logical operators, we can use the and keyword:

number > 4 and number < 10
False

Now we get a single bool: False. The and keyword will only return True if both of the expressions on either side also evaluate to True. We have already seen that number < 10 is False, hence the whole statement evaluates to False.

Rather than and we can also combine logical expressions with the or keyword:

number > 4 or number < 10
True

Using the same expressions as before, we now find that by replacing and with or, the whole statement evaluates to True. The or keyword evalutes to True if either expression on either side is True.

We can summarise these new keywords with some very simple examples:

True and True
True
True and False
False

The and keyword only evalutes to True if both expressions either side of it are also True.

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

The or keyword evalutes to True if either expression on either side of it are True.

What if?#

We now have the capability to test whether or not various expressions are True or False in Python - why should you care?

Imagine you’re playing a simple game with a friend. You think of a number between \(1\) and \(10000\), and then you ask your friend to try and guess that number. If your friend guesses correctly, you congratulate them on their frankly astonishing luck and joke that they might think about buying a lottery ticket. If your friend guesses incorrectly, you give them a pat on the back and wish them better luck the next time they play one of your silly games. Let’s code this up in Python.

my_number = 1298
success = 'Congratulations!'
failure = 'Better luck next time!'

Here we’ve declared most of the variables that we will need, the number that you thought up in your head, and two responses: one for if your friend guesses correctly (success) and one for if they fall short (failure). Now let’s say our imaginary friend guesses 1298:

friend_guess = 1298

We know how to verify if this guess is correct using logical operators:

friend_guess == my_number
True

Sure enough, my hypothetical friend’s luck is just wild. In this case, I want to print the success variable:

print(success)
Congratulations!

The decision making process we just went through manually can be written programmatically with an if statement:

if friend_guess == my_number:
    print(success)
Congratulations!

An if statement can be used to make code conditional. In the example above, the success variable is only printed if friend_guess is equal to my_number. This can be written generically as:

if this_expression_evaluates_to_true:
    do_something

If the expression following the if keyword evaluates to False, then the code indented beneath will not run. If we want to specify what should happen in this case, we can use the else keyword:

friend_guess = 2525

if friend_guess == my_number:
    print(success)

else:
    print(failure)
Better luck next time!

Here we have changed friend_guess to a different number, so friend_guess == my_number is now False and print(success) does not run. If the expression associated with an if statement is False, then the following else block will run instead, here printing the failure message.

Switching to a much less contrived example, sometimes we want to test more than one condition in an if statement:

a = 25

if a < 30 and a % 5 == 0:
    print(f'{a} is less than 30 and divisible by 5.')
25 is less than 30 and divisible by 5.

Reminder

The modulo operator % computes the remainder after dividing one number by another.

We can do this, as shown above, by using the and and or keywords described previously. But what if we want to print a different message if a is less than \(30\) but not divisible by \(5\)?

a = 26

if a < 30 and a % 5 == 0:
    print('25 is less than 30 and divisible by 5.')

elif a < 30:
    print(f'{a} is less than 30 but not divisible by 5.')
26 is less than 30 but not divisible by 5.

Here we have introduced an elif (“else-if”) statement. Our code firstly checks if a is less than \(30\) and divisible by \(5\). Failing that, we then perform a second check to see whether a is less than \(30\). In this example, the statement after the elif keyword evalutes to True, so that block of code runs.

We can add an additional elif block to account for another possibility:

a = 35

if a < 30 and a % 5 == 0:
    print('25 is less than 30 and divisible by 5.')

elif a < 30:
    print(f'{a} is less than 30 but not divisible by 5.')

elif a % 5 == 0:
    print(f'{a} is greater than 30 but divisible by 5.')
35 is greater than 30 but divisible by 5.

And finally, we can add an else block to cover the only other reasonable possibility:

a = 37

if a < 30 and a % 5 == 0:
    print('25 is less than 30 and divisible by 5.')

elif a < 30:
    print(f'{a} is less than 30 but not divisible by 5.')

elif a % 5 == 0:
    print(f'{a} is greater than 30 but divisible by 5.')

else:
    print(f'{a} is greater than 30 and is not divisible by 5.')
37 is greater than 30 and is not divisible by 5.

We are thus able to make our code run differently depending on the value of a.

Exercise#

1.

a) Write a program that can tell whether or not a word has any vowels in it.

b) Extend your program by checking if the word contains no vowels and does not contain the letter t.

2. Imagine that you are playing “Rock, Paper, Scissors” with a friend - they have (unbeknownst to you) picked rock.

Write some Python to decide who wins depending upon your weapon of choice.