Python中and与多个if语句的效率对比

21

在if语句中使用“&&”和使用多个if语句之间是否存在效率差异?换句话说,类似于以下代码:

if expr1 == expr2 and expr3==expr4:
  dostuff()

从效率的角度来看,与...不同:

if expr1 == expr2:
  if expr3 == expr4:
    dostuff()

我进行的基本测试没有发现差异,但是有更多知识(或至少更彻底的测试)的人有一个确定的答案吗?


1
可能与Python的if语句效率有关。 - anijhaw
1
我不知道效率上是否有差异,但更重要的是你的代码可读性。如果使用多个嵌套的 if 语句更清晰明了,那就按照你认为合理的方式去做。 - derekerdmann
1
如果你真的想知道发生了什么,你应该在反汇编器中查看它,但第一个表达式同样快(潜在地更快,但可能没有以那种方式进行优化),因为Python使用短路评估。 - Nick Bastin
@NickBastin 因为运算符是 and,所以短路计算在这两种方法之间有什么区别呢?在嵌套的 if 中,如果第一个条件为假,则不会检查其他条件。短路计算也是如此。 - Astitva Srivastava
5个回答

17

使用 and 和嵌套的 if 之间的速度差异将是很小的。你可能在抓错了树。考虑一下这棵树:

if oftenTrueCondition and rarelyTrueCondition:

相比之下

if rarelyTrueCondition and oftenTrueCondition:

因此,除非第一个条件必须首先评估(它是防止下一个表达式崩溃或执行愚蠢/昂贵操作的保护条件),否则请考虑交换评估顺序。


这是一个非常有用的思考方式,我之前没有考虑过,实际上在数值工作中会派上用场。谢谢! - spencer nelson

14

在性能方面没有太大差异,如果有的话,这不足以影响您的决定。在我看来,这里的决策应该纯粹从易读性的角度出发。第一种方法通常更标准,但有时第二种方法可能会更清晰。选择最能表达您意图的方法。


1
s/IMO//。出于性能原因,在几乎任何语言中选择其中之一都是绝对疯狂的。无法量化哪个更快,实现可以随意操作。 - L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
2
谢谢。通常我同意并且注重可读性而非效率。但在这种特殊情况下,if语句在一个循环中被执行了相当多的次数,而我有一个要求需要减少运行时间,所以至少这一次它可能很重要。 - TimothyAWiseman

7
无论哪种情况,expr1 == expr2if 中都会被评估为 false,因此第二个表达式不会被评估。

5

当你有疑问时,你可以使用dis模块检查Python编译你的语句的方式:

>>> import dis
>>> def test1():
...     if expr1 == expr2 and expr3==expr4:
...        dostuff()
... 
>>> def test2():
...     if expr1 == expr2:
...        if expr3 == expr4:
...           dostuff()
... 
>>> dis.dis(test1)
  2           0 LOAD_GLOBAL              0 (expr1)
              3 LOAD_GLOBAL              1 (expr2)
              6 COMPARE_OP               2 (==)
              9 JUMP_IF_FALSE           24 (to 36)
             12 POP_TOP             
             13 LOAD_GLOBAL              2 (expr3)
             16 LOAD_GLOBAL              3 (expr4)
             19 COMPARE_OP               2 (==)
             22 JUMP_IF_FALSE           11 (to 36)
             25 POP_TOP             

  3          26 LOAD_GLOBAL              4 (dostuff)
             29 CALL_FUNCTION            0
             32 POP_TOP             
             33 JUMP_FORWARD             1 (to 37)
        >>   36 POP_TOP             
        >>   37 LOAD_CONST               0 (None)
             40 RETURN_VALUE        
>>> dis.dis(test2)
  2           0 LOAD_GLOBAL              0 (expr1)
              3 LOAD_GLOBAL              1 (expr2)
              6 COMPARE_OP               2 (==)
              9 JUMP_IF_FALSE           28 (to 40)
             12 POP_TOP             

  3          13 LOAD_GLOBAL              2 (expr3)
             16 LOAD_GLOBAL              3 (expr4)
             19 COMPARE_OP               2 (==)
             22 JUMP_IF_FALSE           11 (to 36)
             25 POP_TOP             

  4          26 LOAD_GLOBAL              4 (dostuff)
             29 CALL_FUNCTION            0
             32 POP_TOP             
             33 JUMP_ABSOLUTE           41
        >>   36 POP_TOP             
             37 JUMP_FORWARD             1 (to 41)
        >>   40 POP_TOP             
        >>   41 LOAD_CONST               0 (None)
             44 RETURN_VALUE        

如您所见,在Python字节码级别上,这两个语句是相同的 - 即使在第一个语句中使用单个if,它也会在第一次比较后执行JUMP_IF_FALSE。


2

第一个方法(使用ifand)更快 :-)

我用timeit测试了一下,结果如下:

Variant 1: 9.82836714316
Variant 2: 9.83886494559
Variant 1 (True): 9.66493159804
Variant 2 (True): 10.0392633241

对于最后两个,第一个比较是True,所以第二个被跳过了。有趣的结果。


import timeit


print "Variant 1: %s" % timeit.timeit("""
for i in xrange(1000):
    if i == 2*i and i == 3*i:
        pass
        """,
        number = 1000)

print "Variant 2: %s" % timeit.timeit("""
for i in xrange(1000):
    if i == 2*i:
        if i == 3*i:
            pass
        """,
        number = 1000)

print "Variant 1 (True): %s" % timeit.timeit("""
for i in xrange(1000):
    if i == i and i == 3*i:
        pass
        """,
        number = 1000)

print "Variant 2 (True): %s" % timeit.timeit("""
for i in xrange(1000):
    if i == i:
        if i == 3*i:
            pass
        """,
        number = 1000)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接