Вопрос

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

ЛБЫЛ (Lоок Bпрежде чем You Lвп):

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

EAFP (это Eчто касается ASK Fсексуальность , чем Pувольнение):

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.Это просто предпочтение?

Это было полезно?

Решение

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

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

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

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

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

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

Исключения обрабатываются более эффективно в 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;при вызове со строкой "2323🄅₅" он выдаст ошибку, а не корректно вернет значение по умолчанию.

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top