Лучше «попробовать» что-нибудь и поймать исключение или проверить, если возможно сначала избежать исключения? - PullRequest
121 голосов
/ 30 сентября 2011

Должен ли я проверить if что-то действительное или просто try, чтобы сделать это и поймать исключение?

  • Есть ли в какой-либо убедительной документации, что предпочтителен один из способов?
  • Это еще один способ питон ?

Например, я должен:

if len(my_list) >= 4:
    x = my_list[3]
else:
    x = 'NO_ABC'

Или:

try:
    x = my_list[3]
except IndexError:
    x = 'NO_ABC'

Некоторые мысли ...
PEP 20 говорит:

Ошибки никогда не должны проходить бесшумно.
Если явно не молчать.

Следует ли использовать try вместо if как ошибку, передаваемую без вывода сообщений? И если да, то вы явно отключаете его, используя его таким образом, поэтому делаете это нормально?


Я не в отношении ситуаций, когда вы можете делать только 1 путь; например:

try:
    import foo
except ImportError:
    import baz

Ответы [ 8 ]

142 голосов
/ 30 сентября 2011

Вы должны предпочесть try/except, чем if/else, если это приведет к ускорению

  • (например, путем предотвращения дополнительных поисков)
  • более чистый код (меньше строк / прощечитать)

Часто они идут рука об руку.


ускорения

В случаепопытка найти элемент в длинном списке с помощью:

try:
    x = my_list[index]
except IndexError:
    x = 'NO_ABC'

попытка, за исключением того, что это лучший вариант, когда index, вероятно, находится в списке, а ошибка IndexError обычно не вызывается.Таким образом, вы избегаете необходимости дополнительного поиска с помощью if index < len(mylist).

Python поощряет использование исключений, , с которыми вы работаете - это фраза из Погружение в Python .Ваш пример не только обрабатывает исключение (изящно), вместо того, чтобы позволить ему молча передавать , но и исключение происходит только в исключительном случае отсутствия индекса (отсюда и слово * 1035)* исключение !).


код очистителя

В официальной документации Python упоминается EAFP : Проще спроситьпрощение, а не разрешение и Роб Найт отмечает, что перехват ошибок вместо их избежания может привести к более чистому и легкому чтению кода.В его примере это выглядит так:

Хуже (LBYL 'посмотри, прежде чем прыгнешь') :

#check whether int conversion will raise an error
if not isinstance(s, str) or not s.isdigit:
    return None
elif len(s) > 10:    #too many digits for int conversion
    return None
else:
    return int(str)

Лучше (EAFP: Проще спроситьпрощение, чем разрешение) :

try:
    return int(str)
except (TypeError, ValueError, OverflowError): #int conversion failed
    return None
17 голосов
/ 30 сентября 2011

В этом конкретном случае вы должны использовать что-то еще целиком:

x = myDict.get("ABC", "NO_ABC")

В общем, хотя: если вы ожидаете частого сбоя теста, используйте if. Если тест обходится дороже, чем просто попытка выполнить операцию и перехват исключения, если он не пройден, используйте try. Если ни одно из этих условий не применимо, делайте все, что читается легче.

8 голосов
/ 22 сентября 2015

Использование try и except напрямую, а не внутри if охраны должно всегда быть выполнено, если есть какая-либо возможность состояния гонки.Например, если вы хотите убедиться, что каталог существует, не делайте этого:

import os, sys
if not os.path.isdir('foo'):
  try:
    os.mkdir('foo')
  except OSError, e
    print e
    sys.exit(1)

Если другой поток или процесс создает каталог между isdir и mkdir, вы выйдете.Вместо этого сделайте следующее:

import os, sys, errno
try:
  os.mkdir('foo')
except OSError, e
  if e.errno != errno.EEXIST:
    print e
    sys.exit(1)

Это будет завершено, только если каталог 'foo' не может быть создан.

7 голосов
/ 30 сентября 2011

Если банально проверить, что-то не получится, прежде чем вы это сделаете, вам, вероятно, следует одобрить это. В конце концов, создание исключений (включая связанные с ними трассировки) занимает время.

Исключения следует использовать для:

  1. неожиданных вещей, или ...
  2. вещей, где вам нужно перепрыгнуть более чем на один уровень логики (например, когда break не позволяет вам продвинуться достаточно далеко), или ...
  3. вещи, в которых вы точно не знаете, что будет обрабатывать исключение раньше времени, или ...
  4. вещи, где досрочная проверка на отказ обходится дорого (по сравнению с простой попыткой операции)

Обратите внимание, что часто реальный ответ - "ни" - например, в вашем первом примере, что вы действительно должны сделать , это просто использовать .get() для обеспечения значения по умолчанию:

x = myDict.get('ABC', 'NO_ABC')
4 голосов
/ 16 октября 2014

Как упоминают другие посты, это зависит от ситуации.Использование try / сопряжено с некоторыми опасностями, за исключением случаев, когда требуется заранее проверить достоверность ваших данных, особенно при использовании их в более крупных проектах.

  • Код в блоке try может иметь шанснанести всевозможные разрушения до того, как исключение будет обнаружено - если вы заранее проверите предварительную проверку с помощью оператора if, вы можете избежать этого.
  • Если код, вызываемый в вашем блоке try, вызывает общий тип исключения, например TypeError или ValueError,на самом деле вы можете не поймать то же исключение, которое ожидали отловить - это может быть что-то еще, что поднимает тот же класс исключений до или после того, как вы дойдете до линии, где может возникнуть ваше исключение.

например, предположим, что вы имели:

try:
    x = my_list[index_list[3]]
except IndexError:
    x = 'NO_ABC'

Ошибка IndexError ничего не говорит о том, произошло ли это при попытке получить элемент index_list или my_list.

4 голосов
/ 30 сентября 2011

Следует ли использовать try вместо if, если интерпретировать ошибку как тихую передачу? И если это так, вы явно отключаете звук, используя его таким образом, и поэтому делаете это нормально?

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

Использование try: except: предпочтительнее в тех случаях, когда логика if: else: более сложна. Простое лучше, чем сложное; комплекс лучше, чем сложный; и просить прощения легче, чем разрешение.

О чем «ошибки никогда не должны молчать» предупреждает, это тот случай, когда код может вызвать исключение, о котором вы знаете, и где ваш дизайн допускает такую ​​возможность, но вы не спроектировали таким образом, чтобы иметь дело с исключение. Явное молчание ошибки, на мой взгляд, будет делать что-то вроде pass в блоке except, что следует делать только с пониманием того, что «бездействие» действительно является правильной обработкой ошибок в конкретной ситуации. (Это один из немногих случаев, когда я чувствую, что комментарий в хорошо написанном коде, вероятно, действительно необходим.)

Однако в вашем конкретном примере ни один из них не подходит:

x = myDict.get('ABC', 'NO_ABC')

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

2 голосов
/ 25 августа 2018

Всякий раз, когда вы используете try/except для потока управления, спрашивайте себя:

  1. Легко ли увидеть, когда блок try успешен, а когда нет -
  2. известно о всех побочных эффектах внутри блока try?
  3. Известно ли вам о всех случаях, когда блок try вызывает исключение?
  4. Если реализация блока try изменится, будет ли ваш поток управления работать так, как ожидалось?

Если ответ на один или несколько из этих вопросов «нет», возможно,много прощения просить;скорее всего, от вашего будущего я.


Пример.Недавно я увидел код в более крупном проекте, который выглядел следующим образом:

try:
    y = foo(x)
except ProgrammingError:
    y = bar(x)

В разговоре с программистом выяснилось, что предполагаемый поток управления:

Если x является целым числом,do y = foo (x).

Если x - список целых чисел, do y = bar (x).

Это сработало, потому что foo сделал запрос к базе данных изапрос был бы успешным, если x было бы целым числом, и выкинул бы ProgrammingError, если x был списком.

Использование try/except - плохой выбор:

  1. Название исключения, ProgrammingError, не выдает фактическую проблему (что x не является целым числом), что затрудняет понимание происходящего.
  2. ProgrammingErrorвозникает во время вызова базы данных, который тратит время.Все было бы по-настоящему ужасно, если бы выяснилось, что foo записывает что-то в базу данных, прежде чем оно генерирует исключение, или изменяет состояние какой-либо другой системы.
  3. Неясно, если ProgrammingError возникает только тогда, когдаx - список целых чисел.Предположим, например, что в запросе к базе данных foo есть опечатка.Это также может поднять ProgrammingError.Следствием этого является то, что bar(x) теперь также вызывается, когда x является целым числом.Это может вызвать загадочные исключения или привести к непредвиденным результатам.
  4. Блок try/except добавляет требование ко всем будущим реализациям foo.Всякий раз, когда мы меняем foo, мы должны теперь подумать о том, как он обрабатывает списки, и убедиться, что он выдает ProgrammingError, а не, скажем, AttributeError или вообще без ошибок.
0 голосов
/ 30 сентября 2011

Для общего значения вы можете прочитать Идиомы и анти-идиомы в Python: исключения .

В вашем конкретном случае, как указано другими, вы должны использовать dict.get():

get (ключ [, по умолчанию])

Возвращает значение для ключа, если ключ находится в словарь, иначе по умолчанию. Если значение по умолчанию не указано, по умолчанию Нет, так что этот метод никогда не вызывает KeyError.

...