Лучше "попробовать" что-то и поймать исключение или проверить, можно ли сначала избежать исключения?



Я должен проверить 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
629   8  

8 ответов:

вы должны предпочесть try/except over if/else если это приведет к

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

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


ускорение

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

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

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

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


чище код

официальная документация 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

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

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

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

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

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

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

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

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

используя 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 ' не может быть создан.

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

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

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

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

однако в вашем конкретном примере ни то, ни другое не подходит:

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

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

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

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

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

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

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

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

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

get (key [, default])

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

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

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

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


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

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

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

если x-целое число, сделайте y = foo (x)

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

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

используя try/except это плохой выбор здесь:

  1. имя исключения ProgrammingError, не отдает актуальную проблему (что x не является целым числом). Это затрудняет понимание того, что происходит.
  2. The ProgrammingError is возникает во время вызова базы данных, что излишне тратит время. Все было бы действительно ужасно, если бы оказалось, что foo записывает что-то в базу данных, прежде чем она выдает исключение или изменяет состояние какой-либо другой системы.
  3. пока неясно, будет ли каждый ProgrammingError причинена x быть список. Предположим, например, что есть опечатка в foo's запрос к базе данных. Это также может вызвать ProgrammingError. Следствием этого является то, что bar(x) теперь также называется if x is целое. Это может вызвать загадочные исключения или привести к непредсказуемым результатам.
  4. The try/except блок добавляет требование ко всем будущим реализации foo. Всякий раз, когда мы меняемся foo, мы сейчас должны думать о том, как он обрабатывает списки и убедитесь, что он бросает!--8-->, а не, скажем,AttributeError или вообще без ошибок.

Comments

    Ничего не найдено.