In this lesson we will take a deeper look at programming logic and if statements, including:
- Logical operators
- De Morgan's law
- Truthy values
- Nested if statements
- Tertiary if statements
Sometimes you might need to take more than one factor into account when you write an if statement. We use
or operators to do this.
The and operator
Suppose we wanted to make lemonade. You need lemons, of course, but that isn't all. You need sugar too. It isn't enough to just have sugar or lemons, you need both.
Here is how we test that there are enough lemons and sugar:
lemons = 12 sugar_kg = 3 if lemons >= 10 and sugar_kg >= 2: print("Let's make lemonade")
The code checks the number of lemons, and the amount of sugar, using the same type of comparison operators we have used before. The
and operator ensures that the if block only runs if both conditions are true.
The or operator
or operator combines two comparisons and gives a true result if either (or both) values are true. Here is an example:
day = 'monday' if day == 'monday' or day == 'thursday': print("It's bath night!")
In this case, the message will be printed if the day is Monday or Thursday.
The not operator
Sometimes you need to check if something is not true. The
not operator can do this. It inverts the sense of the test, swapping true and false. For example:
if x >=1 and not x > 10: print('a is between 1 and 10')
not x > 10 is identical to saying
x <= 10, you can choose either if you think one is easier to understand.
not is sometimes useful, for example with truthy values as we will see later in this tutorial.
With an expression like this:
x = a*3 + b*2
we know, of course, that the multiplications are calculated first, and then the addition. That is because multiplication has higher precedence than addition. The same principle applies to logical expressions:
if lemons >= 10 and sugar_kg >= 2: print("Let's make lemonade")
The comparison operators have higher precedence, so they are calculated first before the
and is evaluated. The program first checks that there are enough lemons, and if there are, it also checks if there is enough sugar - if both are true, the print statement is executed.
Python is quite clever. If there are not enough lemons, it knows the answer will be false (because
and cannot be true if the first term is false), so it doesn't waste time checking the amount of sugar. This is a very useful feature, called short circuit evaluation.
Python has two special values,
False that represent, as you would expect, true and false:
if True: print('This will always print') if False: print('This will never print')
In addition, Python has certain other values that count as
False. These are called truthy values. The rules are fairly simple.
A numerical value counts as
False if it is zero,
True otherwise. So instead of this code:
x = 3 if x!=0: print('x is not zero')
You can write:
x = 3 if x: print('x is not zero')
Although this is optional, most Python programmers will naturally use the second method. When you get used to it, the first method will start to look a bit strange.
If you wanted to check if
x is equal to 0, you can use
x = 0 if not x: print('x is zero')
A collection is any type of data structure that holds other values. An example we have met already is the list type. A string is also a collection (a collection of characters).
As you learn more Python you will meet tuples, dictionaries, sets, and maybe a few more types.
The general rule is that a collection counts as
False if it is empty,
So for example you should never do this:
s = 'abc' if len(s)!=0: print(s)
It is better to do this:
s = 'abc' if s: print(s)
Python has a special value
None that can be used to represent no particular value. For example, if you want to create a variable but you don't yet know what its value should be, you can set it to
None. In Python,
None always counts as
Any other objects that aren't collections will normally count as
True. This includes any types of object that you define yourself, unless you add code to change that behaviour.
Nested if statements
Sometimes it isn't possible to fit the logic into a single if statement. Sometimes they need to be nested.
Imagine a fairground ride has a minimum and maximum height restriction. You can't use the ride if you are less than 1.2m tall, or greater than 1.7m tall. We could check that with code like this:
print('How tall are you?') height = float(input()) if height < 1.2: print('You are not tall enough to use this ride') elif height > 1.7: print('You are too tall to use this ride') else: print('You can use this ride') print('done')
We might enhance this code. If someone enters their height as 1.15, then they are almost tall enough to ride. You might want to suggest that they come back next year (as well as telling them they cannot ride).
print('How tall are you?') height = float(input()) if height < 1.2: print('You are not tall enough to use this ride') if height > 1.15: #1 print('Please come back next year') #2 elif height < 1.7: print('You are too tall to use this ride') else: print('You can use this ride') print('done')
Notice that the new if statement (#1) is inside the block of the original if statement. It will only be called if the height is also less than 1.2, so it detects cases of the height between 1.15 and 1.2. Also, the print statement (#2) marks the end of both if statements, so the code indents move back 8 characters rather than just four.
In principle, you can nest if statement to depths of 3, 4, or whatever you need. But complex, nested if statements can be difficult to follow, and it is easy to make mistakes, which cause bugs. Later we will see how to create our own functions, which can help to make the code much easier to understand.
Tertiary if statements
You will often find yourself writing code a bit like this:
if x < 0: s = 'negative' else: s = 'positive'
The pattern here is that we are setting
s to one value or the other depending on the value of
x. Python provides a neat way of doing this:
s = 'negative' if x < 0 else 'positive':
You should generally consider using this, for several reasons:
- It is more readable because the form of the code makes the intent clear - you are setting a single variable
sto one value or another depending on
- It is less error-prone. In the first form, you have to name
stwice. You might accidentally type
tthe second time and introduce a bug.
- It is shorter.
Another more subtle advantage is that it is an expression. So you could avoid using the variable
s altogether, for example, if you wanted to print the result:
print('negative' if x < 0 else 'positive'):