Подтвердить что ты не робот

Часто задаваемые вопросы по 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, есть много других примеров, где вы просто смотрите исключение вместо проверки типов раньше.)

4b9b3361

Ответ 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)
''')