If I do this:
>>> False in [False, True]
True
That returns True
. Simply because False
is in the list.
Let's see it as a collection containment checking operation: [False, True]
is a list containing some elements.
The expression True in [False, True]
returns True
, as True
is an element contained in the list.
Therefore, not True in [False, True]
gives the "boolean opposite", not
result of the above expression (without any parentheses to preserve precedence, as in
has greater precedence than not
operator).
Therefore, not True
will result False
.
On the other hand, (not True) in [False, True]
, is equal to False in [False, True]
, which is True
(False
is contained in the list).
Operator precedence. in
binds more tightly than not
, so your expression is equivalent to not((True) in [False, True])
.
It's all about operator precedence (in
is stronger than not
). But it can be easily corrected by adding parentheses at the right place:
(not(True)) in [False, True] # prints true
writing:
not(True) in [False, True]
is the same like:
not((True) in [False, True])
which looks if True
is in the list and returns the "not" of the result.
not x in y
is evaluated as x not in y
You can see exactly what's happening by disassembling the code. The first case works as you expect:
>>> x = lambda: False in [False, True]
>>> dis.dis(x)
1 0 LOAD_GLOBAL 0 (False)
3 LOAD_GLOBAL 0 (False)
6 LOAD_GLOBAL 1 (True)
9 BUILD_LIST 2
12 COMPARE_OP 6 (in)
15 RETURN_VALUE
The second case, evaluates to True not in [False, True]
, which is False
clearly:
>>> x = lambda: not(True) in [False, True]
>>> dis.dis(x)
1 0 LOAD_GLOBAL 0 (True)
3 LOAD_GLOBAL 1 (False)
6 LOAD_GLOBAL 0 (True)
9 BUILD_LIST 2
12 COMPARE_OP 7 (not in)
15 RETURN_VALUE
>>>
What you wanted to express instead was (not(True)) in [False, True]
, which as expected is True
, and you can see why:
>>> x = lambda: (not(True)) in [False, True]
>>> dis.dis(x)
1 0 LOAD_GLOBAL 0 (True)
3 UNARY_NOT
4 LOAD_GLOBAL 1 (False)
7 LOAD_GLOBAL 0 (True)
10 BUILD_LIST 2
13 COMPARE_OP 6 (in)
16 RETURN_VALUE
It is evaluating as not True in [False, True]
, which returns False
because True
is in [False, True]
If you try
>>>(not(True)) in [False, True]
True
You get the expected result.
Alongside the other answers that mentioned the precedence of not
is lower than in
, actually your statement is equivalent to :
not (True in [False, True])
But note that if you don't separate your condition from the other ones, python will use 2 roles (precedence
or chaining
) in order to separate that, and in this case python used precedence. Also, note that if you want to separate a condition you need to put all the condition in parenthesis not just the object or value :
(not True) in [False, True]
But as mentioned, there is another modification by python on operators that is chaining:
Based on python documentation :
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.
For example the result of following statement is False
:
>>> True == False in [False, True]
False
Because python will chain the statements like following :
(True == False) and (False in [False, True])
Which exactly is False and True
that is 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
Another famous example is number range :
7<x<20
which is equal to :
7<x and x<20