一位朋友(低级技能级别的娱乐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语句一样多的时间?
答案 0 :(得分:37)
我想说单一测试和单独测试一样快。 Python还使用了所谓的short-circuit evaluation。
对(a and b and c)
来说,如果b
为c
,则a
或false
将不再受到测试。
类似地,如果您有OR
表达式(a or b)
且a
为true
,则永远不会评估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优于嵌套”。
答案 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_B
或function_C
的案例而受益首先),您只需更新funcs
列表。 all
确实发生了短路,就好像你已经将if if a and b and c
拼写出来一样。 (如果函数'或'在一起,请改用内置any
。)
答案 6 :(得分:1)
if (a and b and c)
是假的,那么 a
将会失败,而无需检查b
或c
。
那就是说,我个人认为嵌套条件比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)是隐式的。