ChatGPT解决这个技术问题 Extra ChatGPT

Python if not == vs if !=

What is the difference between these two lines of code:

if not x == 'val':

and

if x != 'val':

Is one more efficient than the other?

Would it be better to use

if x == 'val':
    pass
else:
The better is the one you can read, I doubt your program's bottleneck will be here
This question interests me in the "x not in list" and "not x in list" case
@SomethingSomething they are interpreted identically.
@SomethingSomething reference for my above comment: stackoverflow.com/q/8738388/3001761
@SomethingSomething it's the same for those, too; it's how the syntax is interpreted, it doesn't matter what the two operands are.

C
Community

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 that x!=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.


In practice, any class that has __eq__ inconsistent with __ne__ is flat-out broken.
Please note that it is not always true that not x == y has one more instruction. When I put the code into an if, it turned out that they both have the same number of instructions, just one had POP_JUMP_IF_TRUE and the other POP_JUMP_IF_FALSE (that was the only difference between them, other than using a different COMPARE_OP). When I compiled the code without the ifs, I got what you got.
Another example where == and != are not mutually exclusive is a SQL-like implementation involving null values. In SQL null doesn't return true to != compared to any other value, so python implementations of SQL interfaces may also have the same issue.
I'm beginning to wish I hadn't referred to the possible difference between not == and !=, it seems to be the most interesting part of my answer! I don't think this is the place to dwell on if, why and when that makes sense - see e.g. Why does Python have an __ne__ operator method instead of just __eq__?
R
Red Shift

@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:

https://i.stack.imgur.com/tnf1M.png

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.


I hope everyone knows not to act on this information! Making unreadable changes for a 0.3% improvement -- or even a 10% improvement -- is rarely a good idea, and this kind of improvement is very likely to be evanescent (and not in a good way: very slight changes in the Python runtime could eliminate or even reverse any gain.
@Malvolio In addition, there are different implementations of Python.
M
Moshe Goldberg

In the first one, Python has to execute one more operation 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


J
Jacob Zimmerman

An additional note, since the other answers answered your question mostly correctly, is that if a class only defines __eq__() and not __ne__(), then your COMPARE_OP (!=) will run __eq__() and negate it. At that time, your third option is likely to be a tiny bit more efficient, but should only be considered if you NEED the speed, since it's difficult to understand quickly.


k
kylieCatt
>>> 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.


H
Himanshu Mishra

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.


What do you mean "not operator is dynamic"?
@jonrsharpe I think he means that "not x" will call x.__bool__() [python 3 -- python 2 uses nonzero] and revert the result (see docs.python.org/3/reference/datamodel.html#object.__bool__)
A
Alan Jay Weiner

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


brain interprets "positive" faster than it does "negative" is this from experience or have you read studies about this ? I'm just asking because depending on the code in (do this) or (do that), I find a != b easier to understand.