为什么内联if/else比Python中的.get()更快?

jsa*_*623 14 python performance

我之前正在审查一些代码,并且开发人员编写了内联if/ else而不是a get()来从列表中检索元素(如果它存在)(否则给它一个默认值).我决定timeit在repl 上弹出一些代码,结果很困惑.的if/ else需要1/3的时间get().

这是repl代码,下面是repl中的代码以及后代的结果:

import timeit

D = {"a": 1, "b": 2, "c": 3}

def ef(): return D['a'] if 'a' in D else 1

def gt(): return D.get('a', 1)

print "gt1", timeit.timeit(gt, number=10000)
print "ef1", timeit.timeit(ef, number=10000)
print "ef2", timeit.timeit(ef, number=10000)
print "gt2", timeit.timeit(gt, number=10000)
Run Code Online (Sandbox Code Playgroud)

结果:

gt1 0.0659999847412
ef1 0.0239999294281
ef2 0.0249998569489
gt2 0.0539999008179
Run Code Online (Sandbox Code Playgroud)

以及上述timeit调用的10次迭代的视觉,其中结果已经乘以10000以用于表示目的

10次​​迭代的视觉效果

Mar*_*ers 35

D.get()路径包括属性查找和方法调用:

>>> import dis
>>> D = {"a": 1, "b": 2, "c": 3}
>>> def gt(): return D.get('a', 1)
... 
>>> dis.dis(gt)
  1           0 LOAD_GLOBAL              0 (D)
              3 LOAD_ATTR                1 (get)
              6 LOAD_CONST               1 ('a')
              9 LOAD_CONST               2 (1)
             12 CALL_FUNCTION            2
             15 RETURN_VALUE        
Run Code Online (Sandbox Code Playgroud)

属性lookup(LOAD_ATTR)特别减慢了速度.

如果删除属性查找(并为in测试提供本地工作),则该字段将平均:

>>> def gt_fast(D_get=D.get): return D_get('a', 1)
... 
>>> def ef_fast(D=D): return D['a'] if 'a' in D else 1
... 
>>> timeit.timeit(gt_fast)
0.2174091339111328
>>> timeit.timeit(ef_fast)
0.2139298915863037
Run Code Online (Sandbox Code Playgroud)