Python if语句效率

Den*_*nis 16 python performance

一位朋友(低级技能级别的娱乐python脚本编写者)让我查看一些代码.我注意到他有7个单独的陈述,基本上说.

if ( a and b and c):
    do something
Run Code Online (Sandbox Code Playgroud)

陈述a,b,c都测试了它们的相等或缺乏设定值.当我看到它时,我发现由于测试的性质,我可以将整个逻辑块重写为2个分支,从不超过3个深度并且很少超过第一个级别(进行最罕见的测试)第一).

if a:
    if b:
        if c:
    else:
        if c:
else:
    if b:
        if c:
    else:
        if c:
Run Code Online (Sandbox Code Playgroud)

对我来说,从逻辑上讲,如果你做的更少,更简单的测试更快失败并继续前进,它似乎应该更快.我真正的问题是

1)当我说if if时,如果if为真,那么else会被完全忽略吗?

2)理论上会

如果(a和b和c)

花三倍于单独的if语句会花多少时间?

Fel*_*ing 38

我会说单一测试和单独测试一样快.Python还利用了所谓的短路评估.

这意味着(a and b and c),如果是,那bc将不再进行测试.afalse

类似的,如果你有一个OR表达(a or b)atrue,b不会求.

总而言之,这些条款不会因分离而更快失败.

  • 这比接受的答案更有帮助。通过对参数进行重新排序,我的执行速度提高了 20%,因为其中之一是发生次数不平衡的字典查找 (3认同)

Set*_*son 33

ifelse如果计算结果为true,则语句将跳过括号中的所有内容.应该指出的是,担心这类问题,除非每个程序执行数百万次,否则称为"过早优化",应该避免.如果您的代码使用三个if (a and b and c)语句更清晰,则应将其保留.

  • 同意.不要担心性能,担心可读性.就像Martin Fowler所说:"任何傻瓜都可以编写计算机可以理解的代码......但只有优秀的程序员才能编写人类可以理解的代码." (13认同)
  • 如果您怀疑自己遇到了性能问题,首先要做的是测量代码的速度.然后编写您的替代版本,测量它,并查看您的更改是否实际上使代码更快.*Code Complete*中有一个很棒的部分. (2认同)

Ign*_*ams 19

码:

import dis

def foo():
  if ( a and b and c):
    pass
  else:
    pass

def bar():
  if a:
    if b:
      if c:
        pass

print 'foo():'
dis.dis(foo)
print 'bar():'
dis.dis(bar)
Run Code Online (Sandbox Code Playgroud)

输出:

foo():
  4           0 LOAD_GLOBAL              0 (a)
              3 JUMP_IF_FALSE           18 (to 24)
              6 POP_TOP             
              7 LOAD_GLOBAL              1 (b)
             10 JUMP_IF_FALSE           11 (to 24)
             13 POP_TOP             
             14 LOAD_GLOBAL              2 (c)
             17 JUMP_IF_FALSE            4 (to 24)
             20 POP_TOP             

  5          21 JUMP_FORWARD             1 (to 25)
        >>   24 POP_TOP             

  7     >>   25 LOAD_CONST               0 (None)
             28 RETURN_VALUE        
bar():
 10           0 LOAD_GLOBAL              0 (a)
              3 JUMP_IF_FALSE           26 (to 32)
              6 POP_TOP             

 11           7 LOAD_GLOBAL              1 (b)
             10 JUMP_IF_FALSE           15 (to 28)
             13 POP_TOP             

 12          14 LOAD_GLOBAL              2 (c)
             17 JUMP_IF_FALSE            4 (to 24)
             20 POP_TOP             

 13          21 JUMP_ABSOLUTE           29
        >>   24 POP_TOP             
             25 JUMP_ABSOLUTE           33
        >>   28 POP_TOP             
        >>   29 JUMP_FORWARD             1 (to 33)
        >>   32 POP_TOP             
        >>   33 LOAD_CONST               0 (None)
             36 RETURN_VALUE        
Run Code Online (Sandbox Code Playgroud)

因此,虽然设置相同,但组合表达式的清理速度更快,因为它只在堆栈上留下一个值.

  • 不错的测试!在 python 3.8.3 (macos) 上运行它不再是这种情况,两个函数具有完全相同的输出 (3认同)

chi*_*s42 8

至少在python中,效率仅次于可读性,"Flat优于嵌套".

请参阅Python的Zen


Pau*_*McG 5

如果你担心 b 或 c 是被调用的函数而不是被评估的变量,那么这段代码表明短路是你的朋友:

a = False
def b():
    print "b was called"
    return True

if a and b():
    print "this shouldn't happen"
else:
    print "if b was not called, then short-circuiting works"
Run Code Online (Sandbox Code Playgroud)

印刷

if b was not called, then short-circuiting works
Run Code Online (Sandbox Code Playgroud)

但是,如果您有执行此操作的代码:

a = call_to_expensive_function_A()
b = call_to_expensive_function_B()
c = call_to_expensive_function_C()

if a and b and c:
    do something...
Run Code Online (Sandbox Code Playgroud)

那么您的代码仍在调用所有 3 个昂贵的函数。最好让 Python 成为 Python:

if (call_to_expensive_function_A() and
    call_to_expensive_function_B() and
    call_to_expensive_function_C())
    do something...
Run Code Online (Sandbox Code Playgroud)

它只会根据需要调用尽可能多的昂贵函数来确定整体条件。

编辑

您可以使用all内置的来概括这一点:

# note, this is a list of the functions themselves
# the functions are *not* called when creating this list
funcs = [function_A, function_B, function_C]

if all(fn() for fn in funcs):
    do something
Run Code Online (Sandbox Code Playgroud)

现在,如果您必须添加其他功能,或者想要对它们重新排序(可能function_A非常耗时,并且您可以通过过滤失败function_Bfunction_C首先的案例受益),您只需更新funcs列表。 all做短路就像你已经拼出 if as 一样if a and b and c。(如果函数是“或”在一起的,请改用any内置函数。)