Subsection 6.3.1 And, Or, Not
There are three logical operators: and
, or
, and not
. All three operators take boolean operands and produce boolean values. The semantics (meaning) of these operators is similar to their meaning in English:
x and y
is True
if both x
and y
are True
. Otherwise, and
produces False
.
x or y
yields True
if either x
or y
is True
. Only if both operands are False
does or
yield False
.
not x
yields False
if x
is True
, and vice versa.
Look at the following example. See if you can predict the output. Then, run it to see if your predictions were correct:
Although you can use boolean operators with simple boolean literals or variables as in the above example, they are often combined with the comparison operators, as in this example. Again, before you run this, see if you can predict the outcome:
The expression x > 0 and x < 10
is True
only if x
is greater than 0 and at the same time, x is less than 10. In other words, this expression is True
if x is between 0 and 10, not including the endpoints.
The or
operator is sometimes confusing to new programmers, because it operates differently than the way we use the word ‘or’ when speaking. The sentence, ‘’Karina is going to go to grad school or look for a job in industry’’ suggests that Karina will do one of these two things, but will not do both. In English, the ‘or’ we typically use is what we call an ‘exclusive or’. But in programming, the value of an or
expression is true if both operands are true. The only time an or
expression evaluates to false is when both operands are false. In the Karina example, it would be true if Karina went to grad school, it would be true if Karina got a job in industry, it would be true if Karina went to grad school and got a job in industry. It would only be false if Karina did neither.
Subsection 6.3.2 Smart Evaluation
Python is “smart” about the way it evaluates expressions using boolean operators. Consider the following example:
answer = input('Continue?')
if answer == 'Y' or answer == 'y':
print('Continuing!')
There are two operands for the or
operator here: answer == 'Y'
and 'answer == 'y'
. Python evaluates from left to right, and if the first operand for or
evaluates to True
, Python doesn’t bother evaluating the second operand, because it knows the result must be True
(recall that if either operand for or
is True
, the result is True
). So, if the user enters Y
, Python first evaluates answer ==
'Y'
, determines that it is True
, and doesn’t bother to check to see if answer == 'y'
is True
; it just concludes that the entire condition is True
and executes the print statement.
In a similar fashion, with the and
operator, if the first operand evaluates to False
, Python doesn’t check the second operand’s value, because it can conclude that the result must be False
.
This behavior, in which Python in some cases skips the evaluation of the second operand to and
and or
, is called short-circuit boolean evaluation. You don’t have to do anything to make Python do this; it’s the way Python works. It saves a little processing time. And, as a special bonus, you can take advantage of Python’s short-circuiting behavior to shorten your code. Consider the following example:
This code checks to see if the average weight of a given number of pieces of luggage is greater than 50 pounds. However, there is a potential crash situation here. If the user enters 0
for num_pieces
, the program will crash with a divide by zero error. Try it out to see it happen.
To prevent the crash, you might add an extra if statement to check for zero:
if num_pieces != 0:
if total_weight / num_pieces > 50:
print('Average weight is greater than 50 pounds -> $100 surcharge.')
Now, the division will not occur if num_pieces
is zero, and a potential runtime crash has been averted. Good job!
We can shorten this example to a single if
statement if we do it carefully. Anytime you have two nested if
statements as in the example above, you can combine them into a single if
statement by joining the conditions using the and
operator. Consider the version below, and think about why this if
statement is equivalent in its behavior to the previous version with two nested if
statements:
But wait a minute: is this code safe? Try running the program and entering the value 500
for total_weight
and the value 5
for num_pieces. Then, try it again using the value 0
for num_pieces. There should be no crash.
Next, try altering the code and reversing the order of the if
conditions:
if total_weight / num_pieces > 50 and num_pieces != 0:
print('Average weight is greater than 50 pounds -> $100 surcharge.')
Run the program again, performing the same two tests. This time, you should observe a crash when you enter 0
for num_pieces. Can you analyze why the first version did not crash, but the second one does?
In the second version, when evaluating left-to-right, the division by zero occurs before Python evaluates the comparison num_pieces != 0
. When joining two if
statements into a single if
statement, you must be sure to put the condition from the first if
statement on the left-hand side of the and
operator, and the other condition on the right-hand side, in order to get the same effect.
To summarize this discussion on smart evaluation, keep in mind that when you are performing potentially dangerous operations in an if
statement or while
loop using boolean logic with and
or or
, order matters!
Check your understanding
Checkpoint 6.3.2.
What is the correct Python expression for checking to see if a number stored in a variable x is between 0 and 5.
x > 0 and < 5
Each comparison must be between exactly two values. In this case the right-hand expression < 5 lacks a value on its left.
0 < x < 5
Although most other programming languages do not allow this syntax, in Python, this syntax is allowed. Even though it is possible to use this format, you should not use it all the time. Instead, make multiple comparisons by using and or or.
x > 0 or x < 5
Although this is legal Python syntax, the expression is incorrect. It will evaluate to true for all numbers that are either greater than 0 or less than 5. Because all numbers are either greater than 0 or less than 5, this expression will always be True.
x > 0 and x < 5
Yes, with an ``and`` keyword both expressions must be true so the number must be greater than 0 an less than 5 for this expression to be true.
Checkpoint 6.3.3.
Which of the following may result in a crash at runtime if the user presses Enter without typing a response?
Option A)
yesno = input('Enter Yes or No:')
if yesno[0] == 'Y' and len(yesno) > 0:
print('Yes!')
Option B)
yesno = input('Enter Yes or No:')
if len(yesno) > 0 and yesno[0] == 'Y':
print('Yes!')
Option A
Correct! The comparison yesno[0] == ’Y’ will crash if yesno is an empty string.
Option B
Incorrect. If len(yesno) > 0 is False, the potentially unsafe comparison yesno[0] == ’Y’ will not be evaluated.
Checkpoint 6.3.4.
Consider the following fragment containing a nested if
statement to prevent a crash in the event the user enters an empty response for yesno
:
yesno = input('Enter Yes or No:')
if len(yesno) > 0:
if yesno[0] == 'Y':
print('Yes!')
Which of the following is the correct way to combine the nested if
into a single if
statement that executes identically to the nested if
statements?
Option A)
if yesno[0] == 'Y' and len(yesno) > 0:
print('Yes!')
Option B)
if len(yesno) > 0 and yesno[0] == 'Y':
print('Yes!')
Option C)
if yesno[0] == 'Y' or len(yesno) > 0:
print('Yes!')
Option D)
if len(yesno) > 0 or yesno[0] == 'Y':
print('Yes!')
Option A
Incorrect. The comparison yesno[0] == ’Y’ will crash if yesno is an empty string.
Option B
Correct! Use the and operator to join nested if statements into a single statement, with the first if condition on the left-hand side.
Option C
Incorrect. The comparison yesno[0] == ’Y’ will crash if yesno is an empty string.
Option D
Incorrect. The comparison yesno[0] == ’Y’ will crash if yesno is an empty string.