【问题标题】:Are compound if statements faster, or multiple if statements?复合 if 语句更快还是多个 if 语句?
【发布时间】:2014-03-10 10:05:48
【问题描述】:

假设我们有两段代码:

if foo == True and bar == False and baz == True:
    do something

if foo == True:
    if bar == False:
        if baz == True:
            do something

哪个更快?

【问题讨论】:

  • 没有区别。至少不应该有。
  • 这就是我的想法,因为我相信 Python 在两种情况下都认为一个“不正确”(例如 bar == True)并跳过其余代码后会停止计算表达式。只是想确认一下。
  • 我想在较长的陈述中,由于短路评估,它可能会更快。
  • @DeadChex:两个版本的短路行为完全相同。
  • @paxdiablo 我认为代码读起来好像 foo == True 的原因是为了让我们更容易理解。我确信该程序还有其他内容。

标签: python python-3.x


【解决方案1】:

在我的机器上使用 IPython

In [1]: foo = True
In [2]: bar = False
In [3]: baz = True

In [4]: %%timeit
   ...: if foo and not bar and baz:
   ...:     lambda: None
1000000 loops, best of 3: 265 ns per loop

In [5]: %%timeit
   ...: if foo:
   ...:   if not bar:
   ...:     if baz:
   ...:       lambda: None
1000000 loops, best of 3: 275 ns per loop

如果将其拆分,看起来会有高达 10ns 的开销。如果 10ns 很重要,您可能应该使用另一种语言。

所以对于所有实际目的,不,没有区别。

如果我们再深入一点,我们可以看到这种微小差异的来源。

In [6]: def compound():
   ...:     if foo and not bar and baz:
   ...:         lambda: None
In [7]: def multiple():
   ....:     if foo:
   ....:         if not bar:
   ....:             if baz:
   ....:                 lambda: None

In [8]: import dis
In [9]: dis.dis(compound)
  2           0 LOAD_GLOBAL              0 (foo)
              3 POP_JUMP_IF_FALSE       29
              6 LOAD_GLOBAL              1 (bar)
              9 UNARY_NOT           
             10 POP_JUMP_IF_FALSE       29
             13 LOAD_GLOBAL              2 (baz)
             16 POP_JUMP_IF_FALSE       29

  3          19 LOAD_CONST               1 (<code object <lambda> at 0x101d953b0, file "<ipython-input-9-d057c552d038>", line 3>)
             22 MAKE_FUNCTION            0
             25 POP_TOP             
             26 JUMP_FORWARD             0 (to 29)
        >>   29 LOAD_CONST               0 (None)
             32 RETURN_VALUE   

这有 13 条指令

In [15]: dis.dis(g)
  2           0 LOAD_GLOBAL              0 (foo)
              3 POP_JUMP_IF_FALSE       34

  3           6 LOAD_GLOBAL              1 (bar)
              9 POP_JUMP_IF_TRUE        34

  4          12 LOAD_GLOBAL              2 (baz)
             15 POP_JUMP_IF_FALSE       31

  5          18 LOAD_CONST               1 (<code object <lambda> at 0x101dbb530, file "<ipython-input-10-32b41e5f6f2b>", line 5>)
             21 MAKE_FUNCTION            0
             24 POP_TOP             
             25 JUMP_ABSOLUTE           31
             28 JUMP_ABSOLUTE           34
        >>   31 JUMP_FORWARD             0 (to 34)
        >>   34 LOAD_CONST               0 (None)
             37 RETURN_VALUE  

这有 14 条指令。

我使用系统上的默认 IPython(目前为 2.7.5)执行此操作,但您可以使用此技术对您碰巧运行的任何 Python 版本进行分析。

【讨论】:

  • +1 提供详细的细节,而不仅仅是基准
【解决方案2】:

让我们检查一下字节码看看吧!

>>> def f():
...   if foo == True and bar == False and baz == True:
...     pass
...
>>> def g():
...     if foo == True:
...         if bar == False:
...             if baz == True:
...                 pass
...
>>> dis.dis(f)
  2           0 LOAD_GLOBAL              0 (foo)
              3 LOAD_GLOBAL              1 (True)
              6 COMPARE_OP               2 (==)
              9 POP_JUMP_IF_FALSE       39
             12 LOAD_GLOBAL              2 (bar)
             15 LOAD_GLOBAL              3 (False)
             18 COMPARE_OP               2 (==)
             21 POP_JUMP_IF_FALSE       39
             24 LOAD_GLOBAL              4 (baz)
             27 LOAD_GLOBAL              1 (True)
             30 COMPARE_OP               2 (==)
             33 POP_JUMP_IF_FALSE       39

  3          36 JUMP_FORWARD             0 (to 39)
        >>   39 LOAD_CONST               0 (None)
             42 RETURN_VALUE
>>> dis.dis(g)
  2           0 LOAD_GLOBAL              0 (foo)
              3 LOAD_GLOBAL              1 (True)
              6 COMPARE_OP               2 (==)
              9 POP_JUMP_IF_FALSE       45

  3          12 LOAD_GLOBAL              2 (bar)
             15 LOAD_GLOBAL              3 (False)
             18 COMPARE_OP               2 (==)
             21 POP_JUMP_IF_FALSE       45

  4          24 LOAD_GLOBAL              4 (baz)
             27 LOAD_GLOBAL              1 (True)
             30 COMPARE_OP               2 (==)
             33 POP_JUMP_IF_FALSE       42

  5          36 JUMP_ABSOLUTE           42
             39 JUMP_ABSOLUTE           45
        >>   42 JUMP_FORWARD             0 (to 45)
        >>   45 LOAD_CONST               0 (None)
             48 RETURN_VALUE

几乎一模一样。实际的逻辑部分是完全相同的操作码序列。看起来第二个版本的跳转目标效率略低,因此g 的运行速度可能会稍微慢一些,但这可能会在 Python 版本之间发生变化,而且几乎总是无关紧要。

【讨论】:

    猜你喜欢
    • 2017-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多