2015-02-13 2 views
10

Я просматривал код раньше, и разработчик написал inline if/else, а не get(), чтобы получить элемент из списка, если он существует (в противном случае это значение по умолчанию). Я решил весить код timeit на repl и был довольно смущен результатом. if/else занимает 1/3 времени get().Почему inline if/else быстрее, чем .get() в Python?

Here is the repl code, и ниже приведен код в 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) 

и результаты:

gt1 0.0659999847412 
ef1 0.0239999294281 
ef2 0.0249998569489 
gt2 0.0539999008179 

и визуальный 10 итераций выше timeit звонки, где результат был умножен на 10000 для целей представления

visual of 10 iterations

+2

Почему не было бы быстрее? Я почти всегда ожидал, что встроенный синтаксис языка будет быстрее, чем вызов метода. – Ajedi32

+1

Вы правы в предположении. Я просто ожидал, что встроенная функция будет иметь меньшие накладные расходы в словаре, чем условное, где мы должны ссылаться на значение словаря. – jsanc623

+1

Правильно, но функции, очевидно, придется ссылаться и на значение словаря, верно? В противном случае, как бы вернуть значение, когда ключ существует? – Ajedi32

ответ

28

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   

Поиск атрибута (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 
Смежные вопросы