Python if语句效率

时间:2010-03-29 15:25:39

标签: python performance

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

if ( a and b and c):
    do something

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

if a:
    if b:
        if c:
    else:
        if c:
else:
    if b:
        if c:
    else:
        if c:

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

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

2)理论上会

如果(a和b和c)

与三个单独的if语句一样多的时间?

9 个答案:

答案 0 :(得分:37)

我想说单一测试和单独测试一样快。 Python还使用了所谓的short-circuit evaluation

(a and b and c)来说,如果bc,则afalse将不再受到测试。

类似地,如果您有OR表达式(a or b)atrue,则永远不会评估b

总而言之,这些条款不会更快地失败与分离。

答案 1 :(得分:33)

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

答案 2 :(得分: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)

输出:

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        

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

答案 3 :(得分:8)

至少在python中,效率仅次于可读性,“Flat优于嵌套”。

请参阅The Zen of Python

答案 4 :(得分:3)

我怀疑你会看到一个可衡量的差异所以我建议做任何使代码最具可读性的东西。

答案 5 :(得分:3)

如果你担心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"

打印

if b was not called, then short-circuiting works

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

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...

然后你的代码仍在调用所有3个昂贵的函数。最好让Python成为Python:

if (call_to_expensive_function_A() and
    call_to_expensive_function_B() and
    call_to_expensive_function_C())
    do something...

只会根据需要调用尽可能多的昂贵功能来确定整体状况。

修改

您可以使用内置的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

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

答案 6 :(得分:1)

如果if (a and b and c)是假的,那么

a将会失败,而无需检查bc

那就是说,我个人认为嵌套条件比2 ^ n条件组合更容易阅读。

通常,如果您想确定哪种方式最快,可以使用timeit编写一个简单的基准。

答案 7 :(得分:0)

为了“真正的程序员代码优化”和代码可读性,

if (a and b and c)更快更好。

答案 8 :(得分:0)

只说构造

if a:
  if b:
    if c:
  else:    #note A
    if c:
else:
  if b:
    if c:
  else:    #note A
    if c:

构造不正确。在“((a and b and c)”)构造解释器(以及大多数lang编译代码)中,只需忽略错误值后面的条件,因此“ #note A” else分支不应存在。书写(a,b和c)是隐式的。