Does python prioritize True in ‘or’ and False in ‘and’ condition checks?
Background
I found this meme on LinkedIn recently. After reading this post I hope people feel the same about python.
Experimenting with ‘and’
Let’s assume we have a simple function in python to check if all the numbers in a list are greater than a threshold. Logically the function should return False if any number in the list violates the condition. Therefore, a single False is sufficient to make all([…, False, …]) return a False. Similarly, a single True is sufficient to make any([…, True, …]) return a True. Therefore, if ‘n’ is the length of the list, the number of steps required to determine the output should be strictly less than or equal to n. However, things may become interesting if we use a recursive function instead of a loop to obtain the result. Consider the following function:
It is well known that recursive functions use a “call stack”. Consider the following function call and try to reason how many times print(1) will be executed:
Surprisingly (or unsurprisingly), the answer is — just once! Let’s change the order of conditions under ‘else’ and see what happens:
Surprisingly, the function was called 100 times! Despite the fact that and([A, B]) == and([B, A]), and nums[0] > threshold is violated for the very first index, the recursive call was made to the function. Therefore, we can conclude that the statement on the left is given preference during the evaluation of ‘and’ condition.
Experimenting with ‘or’
Unsurprisingly, the same pattern is observed with True and ‘or’. However, it only gets more mysterious from here.
Enter numpy
More experiments with ‘and’
This arises naturally from the following:
False and A = A and False = False
True and A = A and True = A
More experiments with ‘or’
False or A = A or False = A
True or A = A or True = True
Surprisingly, np.nan or True -> nan! This contradicts A or True = True. At present I’m unable to think of a reasonable explanation for this result.
The mystery does not end there
Mysteriously, all the statements resulted in True / False outputs. The most intriguing results were:
While np.nan.__bool__() -> True explains all these observations, it does not explain why np.nan or True -> nan instead of True (simplified).
Conclusion
Logical ‘or’ and ‘and’ statement in python seem to execute the statement on the left before executing the statement on the right. “A and False = False and A = False” and “True or B = B or True = True” can reduce the number of call stacks in a recursive function if the output is simply ‘and([List])’ or ‘or([List])’.
Handling missing boolean values in numpy is not very intuitive. Using numpy’s logical_and / logical_or may lead to inconsistent outputs that may pass unit tests, but can lead to incorrect results during runtime.
Additional Reading
Please look at the following articles for more interesting observations on python’s handling of logical expressions: