LBYL против EAFP в Java?
недавно я учился на Python и обнаружил идиомы LBYL/EAFP в отношении проверки ошибок Перед выполнением кода. В Python, похоже, принятый стиль-EAFP, и он, похоже, хорошо работает с языком.
LBYL (LООК Bдо You Leap):
def safe_divide_1(x, y):
if y == 0:
print "Divide-by-0 attempt detected"
return None
else:
return x/y
EAFP (это Easier to A sk Forgiveness чем Permission):
def safe_divide_2(x, y):
try:
return x/y
except ZeroDivisionError:
print "Divide-by-0 attempt detected"
return None
мой вопрос таков: Я никогда даже не слышал об использовании EAFP в качестве основной конструкции проверки данных, исходя из фона Java и C++. Является ли EAFP чем-то, что разумно использовать в Java? Или слишком много накладных расходов от исключений? Я знаю, что есть только накладные расходы, когда на самом деле возникает исключение, поэтому я не уверен, почему не используется более простой метод EAFP. Это просто предпочтение?
5 ответов:
лично, и я думаю, что это подкреплено конвенцией, EAFP никогда не является хорошим способом пойти. Вы можете посмотреть на это как на эквивалент следующего:
if (o != null) o.doSomething(); else // handle
против:
try { o.doSomething() } catch (NullPointerException npe) { // handle }
кроме того, рассмотрим следующее:
if (a != null) if (b != null) if (c != null) a.getB().getC().doSomething(); else // handle c null else // handle b null else // handle a null
это может выглядеть намного менее элегантно (и да, это грубый пример-медведь со мной), но это дает вам гораздо большую детализацию в обработке ошибки, в отличие от обертывания все это в try-catch, чтобы получить это
NullPointerException
, а затем попытаться выяснить, где и почему вы его получили.как я вижу, EAFP никогда не следует использовать, за исключением редких ситуаций. Кроме того, поскольку вы подняли этот вопрос: да, блок try-catch действительно несет некоторые накладные расходы даже если исключение не брошено.
Если вы обращаетесь к файлам, EAFP более надежен, чем LBYL, потому что операции, связанные с LBYL, не являются атомарными, и файловая система может меняться между временем, когда вы смотрите, и временем, когда вы прыгаете. На самом деле, стандартное имя TOCTOU - время проверки, Время использования; ошибки, вызванные неточной проверкой, являются ошибками TOCTOU.
рассмотрите возможность создания временного файла, который должен иметь уникальное имя. Лучший способ узнать, существует ли выбранное имя файла еще-это попробовать создать it-убедитесь, что вы используете параметры для обеспечения сбоя операции, если файл уже существует (в терминах POSIX / Unix флаг O_EXCL равен
open()
). Если вы попытаетесь проверить, существует ли файл (возможно, используяaccess()
), то между временем, когда это говорит "нет" и время, когда вы пытаетесь создать файл, кто-то или что-то еще, возможно, создал файл.и наоборот, предположим, что вы пытаетесь прочитать существующий файл. Ваша проверка того, что файл существует (LBYL) может сказать: "это там", но когда вы на самом деле открываете его, вы находите"его там нет".
в обоих этих случаях вы должны проверить окончательную операцию-и LBYL автоматически не помог.
(Если вы возитесь с программами SUID или SGID,
access()
задает другой вопрос; это может иметь отношение к LBYL, но код все равно должен учитывать возможность сбоя.)
в дополнение к относительной стоимости исключений в Python и Java, имейте в виду, что есть разница в философии / отношении между ними. Java пытается быть очень строгим в отношении типов (и всего остального), требуя явных, подробных объявлений сигнатур классов/методов. Он предполагает, что вы должны в любой момент точно знать, какой тип объекта вы используете и что он способен делать. Напротив, "утиный ввод" Python означает, что вы не знаете наверняка (и не должны care) что такое тип манифеста объекта, вам нужно только заботиться о том, что он крякает, когда вы его просите. В такого рода питательную среду, единственный вменяемый отношение предполагать, что все получится, но будьте готовы иметь дело с последствиями, если они не. Природные Ява ограничения не сочетается с такой случайный подход. (Это не имеет целью унизить ни подход, ни язык, а скорее сказать, что эти отношения являются частью идиомы каждого языка и копируют идиомы между разными языками часто может возникнуть неловкость и плохое общение...)
исключения обрабатываются более эффективно в Python, чем в Java, что по крайней мере частично почему вы видите эту конструкцию в Python. В Java более неэффективно (с точки зрения производительности) использовать исключения таким образом.
рассмотрим эти фрагменты кода:
def int_or_default(x, default=0): if x.isdigit(): return int(x) else: return default def int_or_default(x, default=0): try: return int(x) except ValueError: return default
они оба выглядят правильно? Но один из них-нет.
первый, используя LBYL, терпит неудачу из-за тонкого различия между
isdigit
иisdecimal
; при вызове со строкой "①23₅", он будет выдавать ошибку, а не правильно возвращать значение по умолчанию.позже, используя EAFTP, приводит к правильной обработке, по определению. Там нет возможности для поведенческого несоответствия, потому что код, который нуждается требование и код, который утверждает, что требование.
использование LBYL означает принятие внутренней логики и копирование их в call-сайт. Вместо того, чтобы иметь одну каноническую кодировку ваших требований, вы получаете бесплатный шанс испортить каждый раз, когда вы вызываете функцию.
стоит отметить, что EAFTP не об исключениях, и Java-код особенно не должен использовать исключения повсеместно. Речь идет о предоставление правильного задания правому блоку кода. В качестве примера, используя
Optional
возвращаемые значения-это совершенно правильный способ написания кода EAFTP и гораздо более эффективен для обеспечения правильности, чем LBYL.