【问题标题】:Python if not == vs if !=Python if not == vs if !=
【发布时间】:2015-09-10 16:00:53
【问题描述】:

这两行代码有什么区别:

if not x == 'val':

if x != 'val':

一个比另一个更有效吗?

使用会更好

if x == 'val':
    pass
else:

【问题讨论】:

  • 能看懂的更好,我怀疑你程序的瓶颈会在这里
  • 这个问题让我对“x 不在列表中”和“x 在列表中”的情况感兴趣
  • @SomethingSomething 它们的解释相同。
  • @SomethingSomething 参考我的上述评论:stackoverflow.com/q/8738388/3001761
  • @SomethingSomething 也一样;这是语法的解释方式,两个操作数是什么无关紧要。

标签: python if-statement equality


【解决方案1】:

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

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


有人指出in the commments(谢谢@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__ 的实现。每the data model documentation

比较运算符之间没有隐含的关系。这 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 != ynot x == y 更易读

【讨论】:

  • 在实践中,任何具有__eq____ne__ 不一致的类都会被彻底破坏。
  • 请注意,not x == y 有更多指令并不总是为真。当我将代码放入if 时,结果发现它们的指令数量相同,只有一个有POP_JUMP_IF_TRUE,另一个有POP_JUMP_IF_FALSE(这是它们之间的唯一区别,除了使用不同的COMPARE_OP)。当我在没有ifs 的情况下编译代码时,我得到了你所得到的。
  • 另一个==!= 不互斥的示例是涉及null 值的类似SQL 的实现。在 SQL 中,null 不会将 true 返回到 !=,与任何其他值相比,因此 SQL 接口的 python 实现也可能存在相同的问题。
  • 我开始希望我没有提到not ==!= 之间可能存在的区别,这似乎是我回答中最有趣的部分!我不认为这是一个值得考虑的地方,如果,为什么以及何时有意义 - 参见例如Why does Python have an __ne__ operator method instead of just __eq__?
【解决方案2】:

@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 分析器结果:

所以我们可以看到if not x == 'val':if x != 'val': 之间存在约0.7% 的微小差异。其中,if x != 'val': 是最快的。

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

if x == 'val':
        pass
    else:

实际上是最快的,比 if x != 'val': 高了约 0.3%。这不是很可读,但我想如果你想要微不足道的性能改进,可以走这条路。

【讨论】:

  • 我希望每个人都知道不要对这些信息采取行动!为 0.3% 的改进(甚至 10% 的改进)做出不可读的更改很少是一个好主意,而且这种改进很可能是转瞬即逝的(而不是in a good way:Python 运行时中非常微小的更改可能消除甚至逆转任何增益。
  • @Malvolio 另外还有Python的different implementations
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    附加说明,因为其他答案大部分正确地回答了您的问题,如果一个类只定义 __eq__() 而不是 __ne__(),那么您的 COMPARE_OP (!=) 将运行 __eq__() 并否定它。那时,您的第三个选项可能会更高效一些,但只有在您需要速度时才应考虑,因为很难快速理解。

    【讨论】:

      【解决方案5】:
      >>> 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 == yx != y 多一条指令。因此,在大多数情况下,性能差异将非常小,除非您进行数百万次比较,即便如此,这也可能不会成为瓶颈的原因。

      【讨论】:

        【解决方案6】:

        这是关于你的阅读方式。 not 操作符是动态的,这就是为什么你可以在

        中应用它
        if not x == 'val':
        

        != 可以在更好的上下文中被解读为运算符,它的作用与== 的作用相反。

        【讨论】:

        • 什么意思"not操作符是动态的"?
        • @jonrsharpe 我认为他的意思是“not x”会调用 x.__bool__() [python 3 -- python 2 uses nonzero] 并恢复结果(见@ 987654321@)
        【解决方案7】:

        我想扩展我上面的可读性评论。

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

        我想指出的是,大脑解释“积极”的速度比解释“消极”的速度要快。例如,“stop”与“do not go”(由于词数不同,这是一个相当糟糕的例子)。

        所以有一个选择:

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

        优于功能等效的:

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

        较少的可读性/可理解性会导致更多的错误。也许不是在最初的编码中,但(不像你那么聪明!)维护变化......

        【讨论】:

        • 大脑对“积极”的解释比对“消极”的解释要快,这是来自经验还是您阅读过有关这方面的研究?我只是问,因为根据 (do this) 或 (do that) 中的代码,我发现 a != b 更容易理解。
        猜你喜欢
        • 1970-01-01
        • 2011-08-08
        • 2011-02-12
        • 1970-01-01
        • 2011-04-25
        • 1970-01-01
        • 2013-12-23
        相关资源
        最近更新 更多