Today we’ll be talking about Python boolean operators: and, or and not.
In particular we’ll have a look at what exactly they return.
Here you can watch the video version:
Table of Contents
Basic Use of Boolean Operators
First some basics:
The and operator returns True if both operands are true and False otherwise:
>>> True and True # True and True
True
>>> True and False # True and False
False
>>> 2 > 5 and 3 == 2 + 1 # False and True
False
>>> 'a' in 'March' and len("April") > 2 # True and True
True
The or operator returns True if either of the operands is True and False otherwise:
>>> False or True # False or True
True
>>> 2 + 3 != 6 or 'hello'[-1] == 'h' # True or False
True
>>> [1, 2, 3][0] == 0 or 2 < 1 # False or False
False
The not operator returns True if the operand is False and False if it’s True:
>>> not True # not True
False
>>> not 2 + 6 == 8 # not True
False
>>> not ['a', 'b', 'c'][1] == 'c' # not False
True
Short-Circuited Execution
Boolean operators short-circuit execution as soon as the result is known. To see it in action let’s first see what happens if we try to divide by zero:
>>> 3 / 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
And now we’ll use division by zero with boolean operators. This is to demonstrate that portions of code are not executed when not necessary.
First, the and operator:
>>> 3 + 3 == 7 and 3 / 0
False
The second operator is not evaluated at all because the first one is False. And if the first operand is False, it doesn’t matter whether the other is True or False. The result will be False anyway. If we swap the operands, we’ll get an error, because the first operator must be evaluated anyway:
>>> 3 / 0 and 3 + 3 == 7
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
Now, the or operator:
>>> 2 > 5 or 3 / 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
If the first operand evaluates to False, the second must be evaluated too. This is because if the second operand happens to be True, the result will be True. So, at this moment it’s too early to say.
But, if the first operand evaluates to True, then the other one doesn’t have to be evaluated. This is because the result is already known:
>>> len('car') == 3 or 3 / 0
True
Objects Returned by Boolean Operators
Now, what do the and and or operators return? They always return an object. And it doesn’t have to be True or False, which actually are 1 and 0 respectively. They will return the object evaluated on the left side of the operator or the object evaluated on the right side.
The and Operator
Let’s first have a look at the and operator:
The objects are evaluated from left to right. If the first one is True, the result is not yet known. It depends on the other operand, so the other operand is returned:
>>> 2 + 3 and 7 # first: True, second returned
7
>>> 6 and 0 # first: True, second returned
0
If the first operand evaluates to False, the result is already known and the execution is short-circuited. The first operand is returned.
By the way, all numeric zero numbers, all empty objects and the object None are False. Any non-zero numbers and any non-empty objects are True:
>>> [] and 4 # empty list - False
[]
>>> {} and 0 # empty dictionary - False
{}
>>> max(-2, -1, 0) and 3 # on the left we have 0, which is False
0
If there are more operands, the first one that evaluates to False is returned or the last one if all evaluate to True:
>>> 3 and 2 + 2 and [] and 9 and 0
[]
The or Operator
Now let’s have a look at the or operator. The or operator evaluates the operands from left to right and returns the first one which is True or the last one if all are False:
>>> 4 or 5 # 4 – first True
4
>>> 4 or 0 # 4 – first True
4
>>> () or 3 + 5 # 8 – first True
8
>>> [] or 0 # both False
0
>>> None or 2 * 5 or {}
10
The fact that the or operator returns the first operand that evaluates to True can be used in assignments and conditional statements. Here’s an example with an assignment where it can be used to set the value of a variable to a non-zero and non-empty value or a default value:
>>> default = 10
>>> a, b, c, d, e = 5, 0, 6, 9, 0
>>> x = a or default; print(x) # a is evaluated to True and assigned
5
>>> x = b or default; print(x) # b is evaluated to False and default is assigned
10
>>> x = b or e or default; print(x) # b and e are evaluated to False and default is assigned
10
And an example with a conditional statements. The functions in the code all return objects and each object can evaluate to True or False:
>>> def func_a():
... print("Function A executed.")
... return True
...
>>> def func_b():
... print("Function B executed.")
... return False
If the first function returns False, the result is not yet known. So, the other function must be called as well:
>>> if func_b() or func_a():
... print('done')
...
Function B executed.
Function A executed.
done
If the first functions returns True, the result is known and the second function isn’t executed:
>>> if func_a() or func_b():
... print('done')
...
Function A executed.
done
The last example shows us how we can short-circuit execution so that the other function is not called.