【问题标题】:Python efficiency of and vs multiple ifs和与多个 if 的 Python 效率
【发布时间】:2011-04-01 19:00:32
【问题描述】:

在 if 语句中使用 and 与使用多个 if 语句之间有效率差异吗?换句话说,是像

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

那么从效率的角度来看不同:

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

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

【问题讨论】:

  • 我不知道效率有什么不同,但更重要的是你的代码的可读性。如果使用多个嵌套的if 语句更清晰,那么做你认为有意义的事情。
  • 如果你真的想知道发生了什么,你应该在反汇编程序中查看它,但是第一个表达式同样快(可能更快,但可能没有以这种方式优化),因为 Python 使用短-电路评估。
  • @NickBastin 因为运营商是and,短路对这两种方法有何影响?在嵌套if的情况下也是如此,如果第一个条件为假,则不会检查另一个。短路也会发生同样的情况。

标签: python performance conditional


【解决方案1】:

在任何一种情况下,expr1 == expr2if 中的计算结果为false,第二个将不会被计算。

【讨论】:

    【解决方案2】:

    如有疑问,您可以使用 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。

    【讨论】:

      【解决方案3】:

      使用 and 和嵌套 if 之间的任何速度差异都将是最小的。你在叫错树。考虑这棵树:

      if oftenTrueCondition and rarelyTrueCondition:
      

      对比

      if rarelyTrueCondition and oftenTrueCondition:
      

      因此,除非必须首先评估第一个条件(防止下一个表达式崩溃或做一些愚蠢/昂贵的事情),否则请考虑交换评估顺序。

      【讨论】:

      • 一种非常有用的思考方式,我没有考虑过,实际上在数值工作中会派上用场。谢谢!
      【解决方案4】:

      第一个(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)
      

      【讨论】:

        【解决方案5】:

        这还不足以影响您的决定(如果有的话)。 IMO,这里的决定应该纯粹从可读性的角度做出。我认为第一个通常更标准,但在某些情况下,第二个可能更清晰。选择最能传达您的意图的方法。

        【讨论】:

        • s/IMO//。出于性能原因,在几乎任何语言中选择其中一个绝对是疯狂的。没有办法量化哪个会更快,实现可以为所欲为。
        • 谢谢。一般来说,我同意并且我更关注可读性而不是效率。但在这种特殊情况下,if 在循环中被执行了相当多的次数,我需要减少运行时间,所以至少这一次可能很重要。
        猜你喜欢
        • 1970-01-01
        • 2011-02-02
        • 1970-01-01
        • 2015-07-20
        • 1970-01-01
        • 2013-11-22
        • 2015-03-01
        • 1970-01-01
        • 2012-03-05
        相关资源
        最近更新 更多