LBYL против EAFP в Java? - PullRequest
       63

LBYL против EAFP в Java?

58 голосов
/ 01 января 2009

Недавно я изучал Python и обнаружил идиомы LBYL / EAFP в отношении проверки ошибок перед выполнением кода. В Python кажется, что принятым стилем является EAFP, и он хорошо работает с языком.

LBYL ( L ook B efore Y ou L eap ):

def safe_divide_1(x, y):
    if y == 0:
        print "Divide-by-0 attempt detected"
        return None
    else:
        return x/y

EAFP ( это E от A sk F орбита чем P ermission ):

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 ]

120 голосов
/ 01 января 2009

Если вы обращаетесь к файлам, EAFP более надежен, чем LBYL, потому что операции, выполняемые в LBYL, не являются атомарными, и файловая система может меняться между временем просмотра и прыжком. На самом деле стандартное название - TOCTOU - время проверки, время использования; ошибки, вызванные неточной проверкой, являются ошибками TOCTOU.

Рассмотрите возможность создания временного файла, который должен иметь уникальное имя. Лучший способ узнать, существует ли выбранное имя файла, - это попытаться создать его - убедитесь, что вы используете параметры, чтобы гарантировать, что ваша операция завершится неудачно, если файл уже существует (в терминах POSIX / Unix флаг O_EXCL равен open() ). Если вы попытаетесь проверить, существует ли файл уже (возможно, с использованием access()), то между временем, когда это говорит «Нет», и временем, когда вы пытаетесь создать файл, кто-то или что-то еще, возможно, создал файл.

И наоборот, предположим, что вы пытаетесь прочитать существующий файл. Ваша проверка, что файл существует (LBYL), может сказать «он там», но когда вы действительно откроете его, вы обнаружите, что «его там нет».

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

(Если вы связываетесь с программами SUID или SGID, access() задает другой вопрос; он может относиться к LBYL, но в коде все равно необходимо учитывать возможность сбоя.)

46 голосов
/ 03 января 2009

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

10 голосов
/ 01 января 2009

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

8 голосов
/ 01 января 2009

Лично, и я думаю, что это подкреплено соглашением, 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

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

Как мне кажется, EAFP никогда не следует использовать, за исключением редких ситуаций. Кроме того, поскольку вы подняли вопрос: да, блок try-catch действительно несет некоторые накладные расходы , даже если исключение не выдается.

5 голосов
/ 28 апреля 2017

Рассмотрим следующие фрагменты кода:

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; при вызове со строкой «?₅²³?₅» он выдаст ошибку, а не вернет значение по умолчанию.

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

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

Стоит отметить, что EAFTP не относится к исключениям , и в коде Java особенно не следует широко использовать исключения. Речь идет о том, чтобы дать правильную работу правильному блоку кода. Например, использование Optional возвращаемых значений является совершенно правильным способом написания кода EAFTP и намного более эффективно для обеспечения корректности, чем LBYL.

...