Лучше "попробовать" что-то и поймать исключение или проверить, можно ли сначала избежать исключения?
должен ли я проверить 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 ответов
вы должны предпочесть try/except
над if/else
если это приводит к
- speed-ups (например, предотвращая дополнительные поиски)
- более чистый код (меньше строк/легче читать)
часто, они идут рука об руку.
ускорение
в случае попытки найти элемент в длинном списке по:
try:
x = my_list[index]
except IndexError:
x = 'NO_ABC'
попробуйте, за исключением лучшего варианта, когда index
- Это, наверное, в list и IndexError обычно не вызываются. Таким образом, вы избегаете необходимости дополнительного поиска по if index < len(mylist)
.
Python поощряет использование исключений,можно использовать - это фраза из Погружение В Python. Ваш пример не только обрабатывает исключение (изящно), но и не позволяет ему молча пройти, также исключение происходит только в исключительных случай индекса не найден (следовательно, слово исключение!).
более чистый код
официальная документация Python упоминает EAFP: легче просить прощения, чем разрешения и Роб Найт отмечает, что ловить ошибки, а не избегать их, может привести к более чистому, более легкому для чтения кода. Его пример говорит об этом так:
хуже (LBYL ' посмотрите перед вами leap'):
#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
. Если ни одно из этих условий не применяется, перейдите к тому, что читается легче.
Если это тривиально, чтобы проверить, будет ли что-то не так, прежде чем вы это сделаете, вы, вероятно, должны поддержать это. В конце концов, создание исключений (включая связанные с ними трассировки) требует времени.
исключения должны использоваться для:
- вещи, которые являются неожиданными или...
- вещи, где вам нужно прыгать более одного уровня логики (например, где
break
недостаточно далеко), или... - вещи, где вы точно не знаете, что будет обрабатывать исключение раньше времени, или...
- вещи, где проверка досрочно на сбой стоит дорого (по отношению к просто попытке операции)
обратите внимание, что часто реальный ответ " ни " - например, в вашем первом примере, что вы действительно должны 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 / за исключением проверки достоверности ваших данных заранее, особенно при использовании его в больших проектах.
- код в блоке 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 (клавиша[, по умолчанию])
вернуть значение ключа, если ключ находится в словарь, иначе по умолчанию. Если значение по умолчанию не задано, по умолчанию Нет, так что этот метод никогда не поднимает KeyError.
всякий раз, когда вы используете try/except
для потока управления спросите себя:
- легко ли увидеть, когда
try
блок преуспевает и когда он терпит неудачу? - вы знаете все побочные эффекты внутри
try
заблокировать? - вы знаете все случаи, в которых
try
блок выдает исключение? - при осуществлении
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
плохой выбор здесь:
- имя исключения
ProgrammingError
, не выдает актуальную проблему (чтоx
не является целым числом). Это затрудняет понимание того, что происходит. - на
ProgrammingError
is поднятый во время вызова базы данных, который излишне тратит время. Все было бы действительно ужасно, если бы оказалось, чтоfoo
записывает что-то в базу данных, прежде чем она выдает исключение или изменяет состояние какой-либо другой системы. - непонятно ли каждый
ProgrammingError
причиненаx
быть список. Предположим, например, что вfoo
запрос базы данных. Это также может вызватьProgrammingError
. Вследствие этогоbar(x)
теперь также называется ifx
is целое число. Это может привести к загадочным исключениям или непредсказуемым результатам. - на
try/except
блок добавляет требование ко всем будущим реализацииfoo
. Всякий раз, когда мы меняемсяfoo
, мы сейчас должны думать о том, как он обрабатывает списки и убедитесь, что он бросает!--8-->, а не, скажем,AttributeError
или вообще никакой ошибки.