This question already has an answer here:
Why does (1 in [1,0] == True) evaluate to False?
(1 answer)
Closed 7 years ago.
Why is it that these statements work as expected when brackets are used:
>>> (True is False) == False
True
>>> True is (False == False)
True
But it returns False when there are no brackets?
>>> True is False == False
False
Based on python documentation about operator precedence :
Note that comparisons, membership tests, and identity tests, all have the same precedence and have a left-to-right chaining feature as described in the Comparisons section.
So actually you have a chained statement like following :
>>> (True is False) and (False==False)
False
You can assume that the central object will be shared between 2 operations and other objects (False in this case).
And note that its also true for all Comparisons, including membership tests and identity tests operations which are following operands :
in, not in, is, is not, <, <=, >, >=, !=, ==
Example :
>>> 1 in [1,2] == True
False
Python has a unique transitive property when it comes to the comparison operators. It will be easier to see in a simpler case.
if 1 < x < 2:
# Do something
This does what it looks like. It checks if 1 < x and if x < 2. The same thing is happening in your non-parenthesized code.
>>> True is False == False
False
It is checking whether True is False and False == False, only one of which is true.
This is a double inequality which gets expanded as (True is False) and (False == False). See for instance What is the operator precedence when writing a double inequality in Python (explicitly in the code, and how can this be overridden for arrays?)
Python interprets multiple (in)equalities the way you would expect in Math:
In Math a = b = c mean all a = b, b = c and a = c.
So True is False == False means True == False and False == False and True == False, which is False.
For boolean constants, is is equivalent to ==.
Python performs chaining if it encounters operators of same precedence when evaluating an expression.
comparisons, including tests, which all have the same precedence
chain from left to right
The below mentioned operators have the same precedence.
in, not in, is, is not, <, <=, >, >=, <>, !=, ==
So, when Python tries to evaluate the expression True is False == False, it encounters the operators is and == which have the same precedence, so it performs chaining from left to right.
So, the expression True is False == False is actually evaluated as:
(True is False) and (False == False)
giving False as the output.
Related
I found a snippet in our code base that looks kind of useless to me but I suspect that somebody put it there for a reason that eludes me; so I thought I'd ask around. The part is something like this:
someParam = 'nope' # someParam is actually a query result, not a fixed string
if someParam != 'nope' and True or False
In my opinion the and True or False is doing exactly nothing for the evaluation and can be removed since the first part, the equality check, combined with the and True (operator precedence and > or, IIRC) is the same as the first part:
If first part evaluates to True -> True and True -> True
If the first part is False -> False and True -> False
now those results with the appended or False:
True or False -> True
False or False -> False
So the result of the condition would not change if I removed the and True or False, correct? Or am I missing something here?
Also could the behaviour of this change while switching from Python 2 to 3? I think not but I'm not quite sure.
You can remove and True or False from if statement, as it return same result what someParam != 'nope' will give.
You can check it by simple example as:
True and True or False
The output is always True Whereas
False and True or False
The output is always False
As you say, the expression foo and True or False is just a complicated way of converting the "truthiness" of the value foo into an actual boolean:
>>> 'foo' and True or False # non-empty strings are truth-y
True
>>> '' and True or False # empty strings are false-y
False
(You can see the rules for built-in types' truthiness here.)
This is because and and or return one of their operands, per the docs:
The expression x and y first evaluates x; if x is false, its
value is returned; otherwise, y is evaluated and the resulting value
is returned.
The expression x or y first evaluates x; if x is true, its value
is returned; otherwise, y is evaluated and the resulting value is
returned.
and, as you say, the operator precedence for and is higher than or.
That code is totally redundant, for a number of reasons:
Because Python already gives you a built-in way to do that, bool:
>>> bool('foo')
True
>>> bool('')
False
Because foo is already a boolean anyway; != is a comparison, and the result of any comparison is either True or False, so any conversion to boolean is redundant.
Because in a context like if foo:, even if foo wasn't already boolean, Python will automatically evaluate the truthiness of foo rather than only accepting a boolean, so if foo and True or False: and if bool(foo) are both pointless; you can just write if foo:.
And no, none of the above points changed between Python 2 and 3. You can check the equivalent links for all of the above in the new version:
Truthiness
Boolean operations
Operator precedence
bool
Comparisons
Yes, x and True or False can be simplified to just x when x is a boolean value.
It says in python 2.7 docs that or has lower precedence than and. But when I type in idle this:
>>> True and True or False
True
>>> True and False or True
True
>>> True and False
False
Why is the result of this True and False or True expression True?
Higher precedence means that an operator would be evaluated before an operator with lower precedence, like, e.g., in arithmetic, multiplication should evaluated before addition, so 1 + 2 * 3 will result in 7 and not 9.
In your usecase, True and False is evaluated first, giving False. This result is then evaluated with the or operator (i.e., False or True), resulting in True.
In fact, operator precedence has nothing to do with this result; it would be the same wherever you put the parentheses, since or always returns True if either of its arguments are true. So:
True and (False or True) == True and (True) == True
(True and False) or True == (False) or True == True
You statement is asking to do the following
First python evaluates the expression on the left;
Evaluation 1: True and false (Since this evaluates to false python then looks to the or expression)
Evaluation 2: True or false
Which then evaluates to true
You may also want to take a look at Boolean logic and truth tables to assist with understanding how this works.
Highest precedence means where you will put the parentheses
((True and True) or False) # True
((True and False) or True) # True
(True and False) # False
As expected, 1 is not contained by the empty tuple
>>> 1 in ()
False
but the False value returned is not equal to False
>>> 1 in () == False
False
Looking at it another way, the in operator returns a bool which is neither True nor False:
>>> type(1 in ())
<type 'bool'>
>>> 1 in () == True, 1 in () == False
(False, False)
However, normal behaviour resumes if the original expression is parenthesized
>>> (1 in ()) == False
True
or its value is stored in a variable
>>> value = 1 in ()
>>> value == False
True
This behaviour is observed in both Python 2 and Python 3.
Can you explain what is going on?
You are running into comparison operator chaining; 1 in () == False does not mean (1 in ()) == False.
Rather, comparisons are chained and the expression really means:
(1 in ()) and (() == False)
Because (1 in ()) is already false, the second half of the chained expression is ignored altogether (since False and something_else returns False whatever the value of something_else would be).
See the comparisons expressions documentation:
Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).
For the record, <, >, ==, >=, <=, !=, is, is not, in and not in are all comparison operators (as is the deprecated <>).
In general, don't compare against booleans; just test the expression itself. If you have to test against a boolean literal, at least use parenthesis and the is operator, True and False are singletons, just like None:
>>> (1 in ()) is False
True
This gets more confusing still when integers are involved. The Python bool type is a subclass of int1. As such, False == 0 is true, as is True == 1. You therefor can conceivably create chained operations that almost look sane:
3 > 1 == True
is true because 3 > 1 and 1 == True are both true. But the expression:
3 > 2 == True
is false, because 2 == True is false.
1 bool is a subclass of int for historic reasons; Python didn't always have a bool type and overloaded integers with boolean meaning just like C does. Making bool a subclass kept older code working.
As expected, 1 is not contained by the empty tuple
>>> 1 in ()
False
but the False value returned is not equal to False
>>> 1 in () == False
False
Looking at it another way, the in operator returns a bool which is neither True nor False:
>>> type(1 in ())
<type 'bool'>
>>> 1 in () == True, 1 in () == False
(False, False)
However, normal behaviour resumes if the original expression is parenthesized
>>> (1 in ()) == False
True
or its value is stored in a variable
>>> value = 1 in ()
>>> value == False
True
This behaviour is observed in both Python 2 and Python 3.
Can you explain what is going on?
You are running into comparison operator chaining; 1 in () == False does not mean (1 in ()) == False.
Rather, comparisons are chained and the expression really means:
(1 in ()) and (() == False)
Because (1 in ()) is already false, the second half of the chained expression is ignored altogether (since False and something_else returns False whatever the value of something_else would be).
See the comparisons expressions documentation:
Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).
For the record, <, >, ==, >=, <=, !=, is, is not, in and not in are all comparison operators (as is the deprecated <>).
In general, don't compare against booleans; just test the expression itself. If you have to test against a boolean literal, at least use parenthesis and the is operator, True and False are singletons, just like None:
>>> (1 in ()) is False
True
This gets more confusing still when integers are involved. The Python bool type is a subclass of int1. As such, False == 0 is true, as is True == 1. You therefor can conceivably create chained operations that almost look sane:
3 > 1 == True
is true because 3 > 1 and 1 == True are both true. But the expression:
3 > 2 == True
is false, because 2 == True is false.
1 bool is a subclass of int for historic reasons; Python didn't always have a bool type and overloaded integers with boolean meaning just like C does. Making bool a subclass kept older code working.
As expected, 1 is not contained by the empty tuple
>>> 1 in ()
False
but the False value returned is not equal to False
>>> 1 in () == False
False
Looking at it another way, the in operator returns a bool which is neither True nor False:
>>> type(1 in ())
<type 'bool'>
>>> 1 in () == True, 1 in () == False
(False, False)
However, normal behaviour resumes if the original expression is parenthesized
>>> (1 in ()) == False
True
or its value is stored in a variable
>>> value = 1 in ()
>>> value == False
True
This behaviour is observed in both Python 2 and Python 3.
Can you explain what is going on?
You are running into comparison operator chaining; 1 in () == False does not mean (1 in ()) == False.
Rather, comparisons are chained and the expression really means:
(1 in ()) and (() == False)
Because (1 in ()) is already false, the second half of the chained expression is ignored altogether (since False and something_else returns False whatever the value of something_else would be).
See the comparisons expressions documentation:
Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).
For the record, <, >, ==, >=, <=, !=, is, is not, in and not in are all comparison operators (as is the deprecated <>).
In general, don't compare against booleans; just test the expression itself. If you have to test against a boolean literal, at least use parenthesis and the is operator, True and False are singletons, just like None:
>>> (1 in ()) is False
True
This gets more confusing still when integers are involved. The Python bool type is a subclass of int1. As such, False == 0 is true, as is True == 1. You therefor can conceivably create chained operations that almost look sane:
3 > 1 == True
is true because 3 > 1 and 1 == True are both true. But the expression:
3 > 2 == True
is false, because 2 == True is false.
1 bool is a subclass of int for historic reasons; Python didn't always have a bool type and overloaded integers with boolean meaning just like C does. Making bool a subclass kept older code working.