我最近发现了令人惊讶的事情。给定一个不包含密钥d
的字典k
,使用三元运算符尝试检索具有默认返回的项目:
>>> def tern():
... d[k] if k in d else 'foo'
...
>>> timeit.timeit(tern, number=1000000)
0.12342095375061035
比dict的.get()
函数运行得更快:
>>> def get_meth():
... d.get(k, 'foo')
...
>>> timeit.timeit(get_meth, number=1000000)
0.20549297332763672
这对我来说似乎是违反直觉的。我认为三元运算符需要通过dict进行2次搜索(一次测试k in d
),然后另一次搜索d[k]
,而.get
只会尝试检索d[k]
如果失败,请返回'foo'
。
我在一个大字典(一百万个元素)和一个小字母(100)上运行它,两次都是三元组明显更快。幕后发生了什么?
答案 0 :(得分:3)
如果你反汇编这两种方法,你会发现get
有一个额外的CALL_FUNCTION
,与POP_JUMP_IF_FALSE
指令相比,这在python中是昂贵的。
如果在
3 0 LOAD_CONST 1 ('blub')
3 LOAD_GLOBAL 0 (d)
6 COMPARE_OP 6 (in)
9 POP_JUMP_IF_FALSE 22
12 LOAD_GLOBAL 0 (d)
15 LOAD_CONST 1 ('blub')
18 BINARY_SUBSCR
19 JUMP_FORWARD 3 (to 25)
>> 22 LOAD_CONST 2 ('foo')
>> 25 POP_TOP
26 LOAD_CONST 0 (None)
29 RETURN_VALUE
获取方法:
6 0 LOAD_GLOBAL 0 (d)
3 LOAD_ATTR 1 (get)
6 LOAD_CONST 1 ('blub')
9 LOAD_CONST 2 ('foo')
12 CALL_FUNCTION 2 #Expensive call
15 POP_TOP
16 LOAD_CONST 0 (None)
19 RETURN_VALUE
我刚才读过一篇很长的文章,其中有一节描述CALL_FUNCTION
为何如此昂贵:
https://doughellmann.com/blog/2012/11/12/the-performance-impact-of-using-dict-instead-of-in-cpython-2-7-2/