"x not in" vs. "not x in" [duplicate]
I've noticed that both of these work the same:
if x not in list
and if not x in list
.
Is there some sort of difference between the two in certain cases? Is there a reason for having both, or is it just because it's more natural for some people to write one or the other?
Which one am I more likely to see in other people's code?
Solution 1:
The two forms make identical bytecode, as you can clearly verify:
>>> import dis
>>> dis.dis(compile('if x not in d: pass', '', 'exec'))
1 0 LOAD_NAME 0 (x)
3 LOAD_NAME 1 (d)
6 COMPARE_OP 7 (not in)
9 JUMP_IF_FALSE 4 (to 16)
12 POP_TOP
13 JUMP_FORWARD 1 (to 17)
>> 16 POP_TOP
>> 17 LOAD_CONST 0 (None)
20 RETURN_VALUE
>>> dis.dis(compile('if not x in d: pass', '', 'exec'))
1 0 LOAD_NAME 0 (x)
3 LOAD_NAME 1 (d)
6 COMPARE_OP 7 (not in)
9 JUMP_IF_FALSE 4 (to 16)
12 POP_TOP
13 JUMP_FORWARD 1 (to 17)
>> 16 POP_TOP
>> 17 LOAD_CONST 0 (None)
20 RETURN_VALUE
so obviously they're semantically identical.
As a matter of style, PEP 8 does not mention the issue.
Personally, I strongly prefer the if x not in y
form -- that makes it immediately clear that not in
is a single operator, and "reads like English". if not x in y
may mislead some readers into thinking it means if (not x) in y
, reads a bit less like English, and has absolutely no compensating advantages.
Solution 2:
>>> dis.dis(lambda: a not in b)
1 0 LOAD_GLOBAL 0 (a)
3 LOAD_GLOBAL 1 (b)
6 COMPARE_OP 7 (not in)
9 RETURN_VALUE
>>> dis.dis(lambda: not a in b)
1 0 LOAD_GLOBAL 0 (a)
3 LOAD_GLOBAL 1 (b)
6 COMPARE_OP 7 (not in)
9 RETURN_VALUE
when you do "not a in b" it will need be converted for (not in)
so, the right way is "a not in b".
Solution 3:
not x in L
isn't explicitly disallowed because that would be silly. x not in L
is explicitly allowed (though it compiles to the same bytecode) because it's more readable.
x not in L
is what everyone uses, though.
Solution 4:
When you write a not in b
it is using the not in
operator, whereas not a in b
uses the in
operator and then negates the result. But the not in
operator is defined to return the same as not a in b
so they do exactly the same thing. From the documentation:
The operators
in
andnot in
test for collection membership.x in s
evaluates to true ifx
is a member of the collections
, and false otherwise.x not in s
returns the negation ofx in s
.
Similarly there is a is not b
versus not a is b
.
The extra syntax was added because it makes it easier for a human to read it naturally.