Часто задаваемые вопросы по Python: "Насколько быстрыми являются исключения?"
Я просто смотрел FAQ по Python, потому что он упоминался в другом вопросе. Я никогда раньше не смотрел на это подробно, я наткнулся на на этот вопрос: "Как быстро есть исключения?":
Блок try/except чрезвычайно эффективен. На самом деле ловить исключение дорого. В версиях Python до 2.0 было распространено использование этой идиомы:
try:
value = mydict[key]
except KeyError:
mydict[key] = getvalue(key)
value = mydict[key]
Я немного удивился тому, что часть "ловить исключение дорого". Является ли это ссылкой только на те случаи except
, где вы фактически сохраняете исключение в переменной или вообще все except
(в том числе в приведенном выше примере)?
Я всегда думал, что использование таких идиом, как показано, будет очень pythonic, особенно как в Python, "проще просить прощения, чем получить разрешение". Также многие ответы на SO обычно следуют этой идее.
Является ли производительность для ловли Исключениями действительно так плохо? Должен ли кто-то следовать LBYL ( "Посмотрите, прежде чем прыгать" ) в таких случаях?
(Обратите внимание, что Im не напрямую говорит о примере из FAQ, есть много других примеров, где вы просто смотрите исключение вместо проверки типов раньше.)
Ответы
Ответ 1
Захват исключений является дорогостоящим, но исключения должны быть исключительными (читай, не так часто). Если исключения редки, try/catch
быстрее, чем LBYL.
В следующем примере раз поиск словарного ключа с использованием исключений и LBYL, когда ключ существует и когда он не существует:
import timeit
s = []
s.append('''\
try:
x = D['key']
except KeyError:
x = None
''')
s.append('''\
x = D['key'] if 'key' in D else None
''')
s.append('''\
try:
x = D['xxx']
except KeyError:
x = None
''')
s.append('''\
x = D['xxx'] if 'xxx' in D else None
''')
for i,c in enumerate(s,1):
t = timeit.Timer(c,"D={'key':'value'}")
print('Run',i,'=',min(t.repeat()))
Выход
Run 1 = 0.05600167960596991 # try/catch, key exists
Run 2 = 0.08530091918578364 # LBYL, key exists (slower)
Run 3 = 0.3486251291120652 # try/catch, key doesn't exist (MUCH slower)
Run 4 = 0.050621117060586585 # LBYL, key doesn't exist
Если обычный случай не является исключением, try/catch
является "чрезвычайно эффективным" по сравнению с LBYL.
Ответ 2
Стоимость зависит от реализации, очевидно, но я бы не стал беспокоиться об этом. В любом случае, это вряд ли будет иметь значение. Стандартные протоколы вызывают исключения в самых странных местах (думаю, StopIteration
), поэтому вы окружены поднятием и ловом, нравится вам это или нет.
При выборе между LBYL и EAFP беспокоитесь о читабельности кода, вместо того чтобы сосредоточиться на микрооптимизации. Я бы избежал проверки типов, если это возможно, поскольку это может уменьшить общность кода.
Ответ 3
Если случай, когда ключ не найден, более чем исключителен, я бы предложил использовать метод get, который обеспечивает постоянную скорость во всех случаях:
s.append('''\
x = D.get('key', None)
''')
s.append('''\
x = D.get('xxx', None)
''')