问题:Python如果不是== vs如果!=

这两行代码有什么区别:

if not x == 'val':

if x != 'val':

一个比另一个更有效吗?

会更好用吗

if x == 'val':
    pass
else:

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:

回答 0

使用dis一下两个版本生成的字节码:

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   

后者的操作较少,因此效率可能会略高一些。


致谢中指出了(感谢@Quincunx),您所进行的操作if foo != barif not foo == bar操作数量完全相同,只是COMPARE_OP更改并POP_JUMP_IF_TRUE切换为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

在这种情况下,除非每次比较所需的工作量有所不同,否则您根本看不到任何性能差异。


但是,请注意,这两个版本在逻辑上并不总是相同的,因为这将取决于所涉及对象的实现__eq____ne__针对对象的实现。根据数据模型文档

比较运算符之间没有隐含的关系。的真相x==y并不意味着那x!=y是错误的。

例如:

>>> class Dummy(object):
    def __eq__(self, other):
        return True
    def __ne__(self, other):
        return True


>>> not Dummy() == Dummy()
False
>>> Dummy() != Dummy()
True

最后,也许是最重要的一点:通常,两者逻辑上是相同的,x != y但比更具可读性not x == y

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.


回答 1

@jonrsharpe对发生的事情有很好的解释。我以为只是将3个选项中的每一个运行10,000,000次时,就会显示出时间差异(足以显示出一点差异)。

使用的代码:

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)

和cProfile事件探查器结果:

在此处输入图片说明

因此,我们可以看到有间〜0.7%的非常微小差别if not x == 'val':if x != 'val':。其中,if x != 'val':是最快的。

但是,最令人惊讶的是,我们可以看到

if x == 'val':
        pass
    else:

实际上是最快的,胜过if x != 'val':〜0.3%。这不是很容易理解,但是我想如果您想忽略不计的性能改进,可以采用这种方法。

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

enter image description here

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.


回答 2

在第一个Python中,Python必须执行比必要更多的操作(而不是仅检查不等于它,而是必须检查它是否相等,因此还要执行一个操作)。不可能说出一次执行的区别,但是如果执行多次,第二次执行将更有效率。总的来说,我会用第二个,但从数学上讲它们是相同的

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


回答 3

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

在这里您可以看到该not x == y指令比的指令多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.


回答 4

另一个需要注意的是,由于其他答案大多可以正确回答您的问题,因此,如果仅定义一个类__eq__()而不是__ne__(),则您COMPARE_OP (!=)将运行__eq__()并取反它。到那时,您的第三个选择可能会更有效率,但只有在需要速度时才应考虑使用,因为这很难快速理解。

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.


回答 5

这是关于您的阅读方式。not操作符是动态的,这就是为什么您可以将其应用于

if not x == 'val':

但是!=作为一个相反的运算符,可以更好地理解==它。

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.


回答 6

我想在上面扩展我的可读性注释。

再次,我完全同意可读性优先于其他(性能无关紧要)的问题。

我想指出的是,大脑对“阳性”的解释比对“阴性”的解释要快。例如,“停止”与“不走”(由于单词数量的差异,这是一个很糟糕的例子)。

因此有一个选择:

if a == b
    (do this)
else
    (do that)

在功能上等效于:

if a != b
    (do that)
else
    (do this)

较低的可读性/可理解性导致更多的错误。也许不是在初始编码中,而是(不如您聪明!)维护发生了变化…

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…


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。