Использование try vs if в python - PullRequest
110 голосов
/ 02 декабря 2009

Есть ли обоснование, чтобы решить, какую из конструкций try или if использовать при тестировании переменной на наличие значения?

Например, есть функция, которая возвращает список или не возвращает значение. Я хочу проверить результат перед его обработкой. Что из следующего будет более предпочтительным и почему?

result = function();
if (result):
    for r in result:
        #process items

или

result = function();
try:
    for r in result:
        #process items
except TypeError:
    pass;

Похожие обсуждения:

Проверка существования члена в Python

Ответы [ 9 ]

193 голосов
/ 03 декабря 2009

Вы часто слышите, что Python поддерживает стиль EAFP («проще просить прощения, чем разрешения») в стиле LBYL («смотри, прежде чем прыгнуть»). Для меня это вопрос эффективности и читабельности.

В вашем примере (скажем, вместо того, чтобы возвращать список или пустую строку, функция должна была возвращать список или None), если вы ожидаете, что 99% времени result будет действительно содержать что-то итеративное, Я бы использовал try/except подход. Это будет быстрее, если исключения действительно являются исключительными. Если result равен None более чем в 50% случаев, тогда лучше использовать if.

Чтобы подтвердить это несколькими измерениями:

>>> import timeit
>>> timeit.timeit(setup="a=1;b=1", stmt="a/b") # no error checking
0.06379691968322732
>>> timeit.timeit(setup="a=1;b=1", stmt="try:\n a/b\nexcept ZeroDivisionError:\n pass")
0.0829463709378615
>>> timeit.timeit(setup="a=1;b=0", stmt="try:\n a/b\nexcept ZeroDivisionError:\n pass")
0.5070195056614466
>>> timeit.timeit(setup="a=1;b=1", stmt="if b!=0:\n a/b")
0.11940114974277094
>>> timeit.timeit(setup="a=1;b=0", stmt="if b!=0:\n a/b")
0.051202772912802175

Итак, в то время как оператор if всегда стоит вам, установить блок try/except практически бесплатно. Но когда Exception действительно происходит, стоимость намного выше.

Мораль:

  • Совершенно нормально (и «питонно») использовать try/except для управления потоком,
  • но это имеет смысл больше всего, когда Exception на самом деле исключительны.

Из документов Python:

ЭСПЦ

Проще просить прощения, чем разрешение. Это обычное Python-кодирование стиль предполагает наличие действительного ключи или атрибуты и ловит исключения, если предположение оказывается ложный. Этот чистый и быстрый стиль характеризуется наличием многих try и except операторы. техника контрастирует с LBYL стиль, общий для многих других языков такие как C.

13 голосов
/ 03 декабря 2009

Ваша функция не должна возвращать смешанные типы (то есть список или пустую строку). Он должен возвращать список значений или просто пустой список. Тогда вам не нужно будет ничего проверять, т. Е. Ваш код сворачивается в:

for r in function():
    # process items
10 голосов
/ 03 декабря 2009

Пожалуйста, игнорируйте мое решение, если код, который я предоставляю, на первый взгляд неочевиден, и вы должны прочитать объяснение после примера кода.

Могу ли я предположить, что «значение не возвращено» означает, что возвращаемое значение равно None? Если да, или если «no value» - False по логическому значению, вы можете сделать следующее, так как ваш код по существу обрабатывает «no value» как «not iterate»:

for r in function() or ():
    # process items

Если function() возвращает что-то, что не соответствует True, вы выполняете итерацию по пустому кортежу, т. Е. Не выполняете никаких итераций. Это по сути LBYL.

5 голосов
/ 03 декабря 2009

Ваш второй пример не работает - код никогда не вызовет исключение TypeError, так как вы можете перебирать как строки, так и списки. Итерации по пустой строке или списку также допустимы - они будут выполнять тело цикла ноль раз.

4 голосов
/ 03 декабря 2009

Что из перечисленного было бы более предпочтительным и почему?

В этом случае предпочтительнее «Look Before You Leap». При исключительном подходе ошибка TypeError может возникнуть в любом месте тела цикла, и она может быть захвачена и выброшена, что не является тем, что вам нужно, и затруднит отладку.

(Я согласен с Брэндоном Корфманом: возвращение None для «no items» вместо пустого списка нарушено. Это неприятная привычка Java-кодеров, которую не следует видеть в Python. Или Java.)

4 голосов
/ 03 декабря 2009

Как правило, у меня сложилось впечатление, что исключения должны быть зарезервированы для исключительных обстоятельств. Если ожидается, что result никогда не будет пустым (но может быть, например, если диск сломался и т. Д.), Второй подход имеет смысл. Если, с другой стороны, пустое значение result является вполне разумным при нормальных условиях, проверка его с помощью оператора if имеет больше смысла.

Я имел в виду (более распространенный) сценарий:

# keep access counts for different files
file_counts={}
...
# got a filename somehow
if filename not in file_counts:
    file_counts[filename]=0
file_counts[filename]+=1

вместо эквивалента:

...
try:
    file_counts[filename]+=1
except KeyError:
    file_counts[filename]=1
3 голосов
/ 03 декабря 2009

bobince мудро указывает, что перенос второго регистра также может перехватывать TypeErrors в цикле, а это не то, что вам нужно. Если вы действительно хотите использовать попытку, вы можете проверить, является ли она итеративной, перед циклом

result = function();
try:
    it = iter(result)
except TypeError:
    pass
else:
    for r in it:
        #process items

Как видите, это довольно некрасиво. Я не предлагаю это, но это должно быть упомянуто для полноты.

1 голос
/ 03 декабря 2009

Что касается производительности, используйте блок try для кода, который обычно не вызывает исключения быстрее, чем использование оператора if каждый раз. Итак, решение зависит от вероятности исключительных случаев.

0 голосов
/ 03 декабря 2009

Как правило, вы должны никогда использовать try / catch или любые другие средства обработки исключений для управления потоком. Несмотря на то, что закулисная итерация контролируется с помощью исключения StopIteration, вы все равно должны предпочесть свой первый фрагмент кода второму.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...