Лучше "попробовать" что-то и поймать исключение или проверить, можно ли сначала избежать исключения?
Я должен проверить 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/exceptoverif/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. Если ни одно из этих условий не применяется, идите с тем, что читается легче.
Если это тривиально, чтобы проверить, будет ли что-то не так, прежде чем вы это сделаете, вы, вероятно, должны одобрить это. В конце концов, построение исключений (включая связанные с ними трассировки) требует времени.
исключения должны использоваться для:
- вещи, которые являются неожиданными или...
- вещи, где вам нужно перейти более чем на один уровень логики (например, где a
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 / 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для управления потоком, спросите себя:
- легко ли увидеть, когда
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не является целым числом). Это затрудняет понимание того, что происходит.- The
ProgrammingErroris возникает во время вызова базы данных, что излишне тратит время. Все было бы действительно ужасно, если бы оказалось, чтоfooзаписывает что-то в базу данных, прежде чем она выдает исключение или изменяет состояние какой-либо другой системы.- пока неясно, будет ли каждый
ProgrammingErrorпричиненаxбыть список. Предположим, например, что есть опечатка вfoo's запрос к базе данных. Это также может вызватьProgrammingError. Следствием этого является то, чтоbar(x)теперь также называется ifxis целое. Это может вызвать загадочные исключения или привести к непредсказуемым результатам.- The
try/exceptблок добавляет требование ко всем будущим реализацииfoo. Всякий раз, когда мы меняемсяfoo, мы сейчас должны думать о том, как он обрабатывает списки и убедитесь, что он бросает!--8-->, а не, скажем,AttributeErrorили вообще без ошибок.
Comments