490

If I do this:

>>> False in [False, True]
True

That returns True. Simply because False is in the list.

But if I do:

>>> not(True) in [False, True]
False

That returns False. Whereas not(True) is equal to False:

>>> not(True)
False

Why?

wjandrea
  • 28,235
  • 9
  • 60
  • 81
Texom512
  • 4,785
  • 3
  • 16
  • 16

8 Answers8

747

Operator precedence 2.x, 3.x. The precedence of not is lower than that of in. So it is equivalent to:

>>> not ((True) in [False, True])
False

This is what you want:

>>> (not True) in [False, True]
True

As @Ben points out: It's recommended to never write not(True), prefer not True. The former makes it look like a function call, while not is an operator, not a function.

wizzwizz4
  • 6,140
  • 2
  • 26
  • 62
Yu Hao
  • 119,891
  • 44
  • 235
  • 294
  • 288
    @Texom512: I would also recommend never writing `not(True)`; prefer `not True`. The first makes it look like a function call, which is where your confusion came from; if `not` was a function, then `not(True) in ...` couldn't possibly be `not ((True) in ...)`. You have to know it's an operator (or you end up in situations like this), so you should write it like an operator, not disguise it as a function. – Ben Jul 15 '15 at 05:40
  • 8
    Also, if you're going to use spacing to indicate precedence for the benefit of the reader, first make sure you're right. It's probably OK to write `a + b*c + d`, it's very bad to write `a+b * c+d`. So `not(True)` is bad by that measure too. – Steve Jessop Jul 15 '15 at 20:47
  • 38
    Actually, **never** write `not True`. Write `False` instead. – Darkhogg Jul 21 '15 at 12:29
  • 16
    Presumably in real life you wouldn't be writing `not True`, you'd be writing something like `not myfunc(x,y,z)` where `myfunc` is some function that returns `True` or `False`. – Nate C-K Jul 22 '15 at 03:17
  • 1
    @YuHao I would enjoy your explanation as to [why you reversed my edits](http://stackoverflow.com/posts/31421411/revisions), as I believe they help readers better understand what's going on. – Ky - Jul 22 '15 at 14:28
  • 1
    @YuHao yes, the parentheses in `(True)` are redundant. However, they were in the original question, so taking them out actually makes it more confusing, since it's harder to tell how your answer relates to the OP's question. Additionally, they weren't in your recommended fix ("This is what you want:"), so I wasn't intending them to be something anyone SHOULD do, just a visual aid to relate it to the problem code. – Ky - Jul 22 '15 at 22:23
  • 4
    @BenC.R.Leggiero That's what I did in [the original answer](http://stackoverflow.com/revisions/31421411/1), and others have corrected it. The current version is clear enough for me, I don't think it's hard to understand without the redundant parentheses, since the key problem has been pointed out, understanding the rest is a programmer's basic skill. – Yu Hao Jul 23 '15 at 01:51
77

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        
Roshan Mathews
  • 5,788
  • 2
  • 26
  • 36
  • 14
    There is always a guy with `dis` but this is a very valuable answer because it shows that actually `not in` is used – jamylak Jul 15 '15 at 08:24
  • 23
    Bytecode is an implementation detail of the CPython interpreter. This is a CPython answer to a Python question, in fact it can be better answered from the language reference directly. – wim Jul 15 '15 at 10:19
  • 5
    @wim I would argue that the bytecode implementation is not as important as the actual disassembly. Other implementations are guaranteed to generate something functionally identical, so understanding one disassembly offers enough insight to understand the "why" and not the lowlevel "how". – Alex Pana Jul 16 '15 at 08:59
37

Operator precedence. in binds more tightly than not, so your expression is equivalent to not((True) in [False, True]).

mooiamaduck
  • 2,066
  • 12
  • 13
34

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.

Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129
14

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.

user3636636
  • 2,409
  • 2
  • 16
  • 31
13

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   
Mazdak
  • 105,000
  • 18
  • 159
  • 188
6

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).

Nick Louloudakis
  • 5,856
  • 4
  • 41
  • 54
6

To clarify on some of the other answers, adding parentheses after a unary operator does not change its precedence. not(True) does not make not bind more tightly to True. It's just a redundant set of parentheses around True. It's much the same as (True) in [True, False]. The parentheses don't do anything. If you want the binding to be more tight, you have to put the parentheses around the whole expression, meaning both the operator and the operand, i.e., (not True) in [True, False].

To see this another way, consider

>>> -2**2
-4

** binds more tightly than -, which is why you get the negative of two squared, not the square of negative two (which would be positive four).

What if you did want the square of negative two? Obviously, you'd add parentheses:

>>> (-2)**2
4

However, it's not reasonable to expect the following to give 4

>>> -(2)**2
-4

because -(2) is the same as -2. The parentheses do absolutely nothing. not(True) is exactly the same.

asmeurer
  • 86,894
  • 26
  • 169
  • 240
  • One of the stranger gotcha's of Perl is that it does indeed change the precedence when the following `(` is present, even for built-in operators, so that `return ($x+$y)*$z` means `(return($x+$y))*$z`. So it's understandable that someone who's used to Perl might get tripped up by this. – Martin Kealey Aug 10 '22 at 07:50