Использование try vs, если в python

Есть ли обоснование, чтобы решить, какую конструкцию из 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

Ответ 1

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

В вашем примере (скажем, что вместо того, чтобы возвращать список или пустую строку, функция должна была возвращать список или 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, стоимость намного выше.

Мораль:

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

В документах Python:

ЭСПЦ

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

Ответ 2

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

for r in function():
    # process items

Ответ 3

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

Можно ли предположить, что значение "нет значения" означает, что возвращаемое значение равно None? Если да, или если значение "нет" False логически, вы можете сделать следующее, поскольку ваш код по существу относится к "нет значения" как "не итерации":

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

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

Ответ 4

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

Ответ 5

Как правило, впечатление, которое я получил, заключается в том, что исключения должны быть зарезервированы для исключительных обстоятельств. Если ожидается, что 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

Ответ 6

Какое из следующих было бы более предпочтительным и почему?

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

(Я согласен с Брэндоном Корфманом: return None для "нет элементов вместо пустого списка". Это неприятная привычка Java-кодеров, которые не должны встречаться на Python или Java.)

Ответ 7

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

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

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

Ответ 8

Что касается производительности, используйте блок try для кода, который обычно does not raise exceptions быстрее, чем использование инструкции if everytime. Таким образом, решение зависит от вероятности случайных случаев.

Ответ 9

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