What is the difference between these two lines of code:
if not x == \'val\':
and
if x != \'val\':
Is one
@jonrsharpe has an excellent explanation of what's going on. I thought I'd just show the difference in time when running each of the 3 options 10,000,000 times (enough for a slight difference to show).
Code used:
def a(x):
if x != 'val':
pass
def b(x):
if not x == 'val':
pass
def c(x):
if x == 'val':
pass
else:
pass
x = 1
for i in range(10000000):
a(x)
b(x)
c(x)
And the cProfile profiler results:
So we can see that there is a very minute difference of ~0.7% between if not x == 'val':
and if x != 'val':
. Of these, if x != 'val':
is the fastest.
However, most surprisingly, we can see that
if x == 'val':
pass
else:
is in fact the fastest, and beats if x != 'val':
by ~0.3%. This isn't very readable, but I guess if you wanted a negligible performance improvement, one could go down this route.
Using dis to look at the bytecode generated for the two versions:
not ==
4 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 2 (==)
9 UNARY_NOT
10 RETURN_VALUE
!=
4 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 3 (!=)
9 RETURN_VALUE
The latter has fewer operations, and is therefore likely to be slightly more efficient.
It was pointed out in the commments (thanks, @Quincunx) that where you have if foo != bar
vs. if not foo == bar
the number of operations is exactly the same, it's just that the COMPARE_OP
changes and POP_JUMP_IF_TRUE
switches to POP_JUMP_IF_FALSE
:
not ==
:
2 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 2 (==)
9 POP_JUMP_IF_TRUE 16
!=
2 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 3 (!=)
9 POP_JUMP_IF_FALSE 16
In this case, unless there was a difference in the amount of work required for each comparison, it's unlikely you'd see any performance difference at all.
However, note that the two versions won't always be logically identical, as it will depend on the implementations of __eq__
and __ne__
for the objects in question. Per the data model documentation:
There are no implied relationships among the comparison operators. The truth of
x==y
does not imply thatx!=y
is false.
For example:
>>> class Dummy(object):
def __eq__(self, other):
return True
def __ne__(self, other):
return True
>>> not Dummy() == Dummy()
False
>>> Dummy() != Dummy()
True
Finally, and perhaps most importantly: in general, where the two are logically identical, x != y
is much more readable than not x == y
.
>>> from dis import dis
>>> dis(compile('not 10 == 20', '', 'exec'))
1 0 LOAD_CONST 0 (10)
3 LOAD_CONST 1 (20)
6 COMPARE_OP 2 (==)
9 UNARY_NOT
10 POP_TOP
11 LOAD_CONST 2 (None)
14 RETURN_VALUE
>>> dis(compile('10 != 20', '', 'exec'))
1 0 LOAD_CONST 0 (10)
3 LOAD_CONST 1 (20)
6 COMPARE_OP 3 (!=)
9 POP_TOP
10 LOAD_CONST 2 (None)
13 RETURN_VALUE
Here you can see that not x == y
has one more instruction than x != y
. So the performance difference will be very small in most cases unless you are doing millions of comparisons and even then this will likely not be the cause of a bottleneck.
In the first one Python has to execute one more operations than necessary(instead of just checking not equal to it has to check if it is not true that it is equal, thus one more operation). It would be impossible to tell the difference from one execution, but if run many times, the second would be more efficient. Overall I would use the second one, but mathematically they are the same
I want to expand on my readability comment above.
Again, I completely agree with readability overriding other (performance-insignificant) concerns.
What I would like to point out is the brain interprets "positive" faster than it does "negative". E.g., "stop" vs. "do not go" (a rather lousy example due to the difference in number of words).
So given a choice:
if a == b
(do this)
else
(do that)
is preferable to the functionally-equivalent:
if a != b
(do that)
else
(do this)
Less readability/understandability leads to more bugs. Perhaps not in initial coding, but the (not as smart as you!) maintenance changes...
It's about your way of reading it. not
operator is dynamic, that's why you are able to apply it in
if not x == 'val':
But !=
could be read in a better context as an operator which does the opposite of what ==
does.