исключения + сигнализация конца итератора: почему это плохо в Java и нормально в Python? - PullRequest
11 голосов
/ 18 октября 2011

Я действительно запутался: стандартный подход в Java - генерировать исключения только в «нештатных» условиях, а не использовать их для оповещения об окончании итератора.

примеры: эффективная Java, пункт 57 («Использовать исключения только для исключительных условий») и информационный бюллетень JavaSpecialists 162 :

Контроль потока

Мы никогда не должны вызывать исключение, которое иначе можно предотвратить. Я видел код, в котором вместо проверки границ предполагается, что данные будут правильными, а затем будут обнаружены исключения RuntimeException:

Вот пример плохого кода (пожалуйста, не кодируйте так):

public class Antipattern1 {
   public static void main(String[] args) {
     try {
       int i = 0;
       while (true) {
         System.out.println(args[i++]);
       }
     } catch (ArrayIndexOutOfBoundsException e) {
       // we are done
    }
  }
}

, тогда как является стандартом для использования этой идиомы в Python, например StopIteration

исключение StopItered

Вызывается методом next () итератора, чтобы сигнализировать об отсутствии дополнительных значений. Это происходит из Exception, а не StandardError, так как это не считается ошибкой в ​​обычном приложении.

Почему это плохо для Java, но хорошо для Python?

Ответы [ 7 ]

6 голосов
/ 18 октября 2011

Python и Java имеют совершенно разные подходы к исключениям. В Python исключения являются нормальными. Посмотрите EAFP (Проще просить прощения, чем разрешения) в глоссарии Python. Также проверьте, что говорит Википедия .

StopIteration - это всего лишь пример EAFP - просто продолжайте и получите следующую вещь от итератора, а в случае неудачи обработайте ошибку.

Если код более читабелен с нелокальным выходом, в Python вы используете исключение. Вы не пишете чеки, вы просто справляетесь со сбоями, если что-то не получается. В этом нет абсолютно ничего постыдного, на самом деле это поощряется. В отличие от Java.


Теперь для конкретного случая StopIteration: рассмотрим функции генератора .

def generator():
    yield 1
    print('Side effect')
    yield 2

Для поддержки какого-либо метода has_next() генератор должен будет проверить следующее значение, вызвав print, прежде чем запрашивается 2. Значение (или повышенное исключение) должно быть запомнено в итераторе. Если has_next был вызван дважды, только первый вызовет побочный эффект. Или следующее значение всегда может быть предварительно вычислено, даже если оно не нужно.

Я считаю семантику Python - вычисление только тогда, когда необходимо следующее значение - лучшей альтернативой.

Конечно, в Java нет возобновляемых генераторов, поэтому здесь сложно сравнивать. Но это некий пример, свидетельствующий о том, что StopItered обобщает лучше, чем hasNext ().

2 голосов
/ 18 октября 2011

Нет ничего, что мешает использовать подобные исключения в Java, это выглядит некрасиво, по крайней мере, для разработчика Java.

Основная причина в том, что трассировки стека из исключений дороги, и, возможно, также, что разработчики Java могут быть немного более обеспокоены о затратах вычислительных ресурсов, чем разработчики Python.

Ява также является довольно "чистым" языком - некоторые скажут, фундаменталистский, что является одной из причин, почему это хороший язык. (* см. комментарий)

Во всяком случае. Фундаменталисты (и некоторые нормальные люди) считают, что использование исключений для нормального потока - это не верный путь ...: -)

Но кроме того, последние версии jvm обнаруживают, что вы генерируете много стековых трасс для той же части кода, и на самом деле будет генерировать исключения без них "через некоторое время", чтобы ускорить процесс.

1 голос
/ 18 октября 2011

Причина, по которой он не рекомендуется, заключается в том, что в java обработка исключений, как правило, обходится дорого для обработки и восстановления. Когда генерируется исключение, он заставляет jvm вернуться назад к тому, что он делал, чтобы обеспечить трассировку стека, что никогда не сказывается на производительности. Короче говоря, это неправильное использование языка - обычно будет более чистый и эффективный способ обработки логики. Рассмотрим следующий код:

try {

    int x = service.getValue();

    if(x > 5)
        throw new NumberTooBigException("Too Big");
    else
        throw new NumberTooSmallException("Too Small");

} catch (NumberTooBigException e) {

    System.out.println("i think it's too big...");

} catch (NumberTooSmallException e) {

    System.out.println("i think it's too small...");

}

Лучше всего использовать только логику управления, предусмотренную Java:

if(x > 5)
    System.out.println("i think it's too big...");
else
    System.out.println("i think it's too small...");

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

String[] args = {"one", "two", "three"};
for(String arg : args)
    System.out.println(args);
}

Исключения лучше использовать, когда что-то идет не так, как IOException («на устройстве не осталось места»), ClassNotFoundException («не удается найти ваш код для запуска») или NoRouteToHostException («Не удается подключиться к хосту») ).

1 голос
/ 18 октября 2011

Java иногда также делает это : «Все реализации DataInput методов используют EOFException вместо возвращаемых значений».В этом случае невозможно использовать обычное значение часового, -1.

.
0 голосов
/ 11 июля 2018

Исключения в Java фиксируют стек выполнения, который чрезвычайно медленный: Насколько медленны исключения Java?

Если вы используете исключение в потоке управления, передайте null как Throwable.В противном случае следующий стандартный библиотечный код Java будет вызываться через иерархию вызовов super:

public Throwable() {
    fillInStackTrace();
}

public synchronized Throwable fillInStackTrace() {
    if (stackTrace != null ||
        backtrace != null /* Out of protocol state */ ) {
        fillInStackTrace(0);
        stackTrace = UNASSIGNED_STACK;
    }
    return this;
}

private native Throwable fillInStackTrace(int dummy);
0 голосов
/ 18 октября 2011

Нет правильного или неправильного ответа на этот вопрос.Обработка исключений - это нейтральная конструкция потока управления, и наилучшее ее использование зависит от контекста и стиля.

В этом случае и Java, и Python выполняют одно и то же по тем же причинам: java.util.Iteratornext() использует NoSuchElementException для оповещения об окончании итератора.Это просто хороший стиль для обоих языков: альтернатива использования специального возвращаемого значения часового по разным причинам гораздо хуже.

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

0 голосов
/ 18 октября 2011

StopIteration существует в Python для простой итерации по любой последовательности.

Использование исключения для реализации этого было дизайнерским выбором, и это на самом деле не противоречит менталитету исключений Java. Метод next() итератора, вызываемый, когда больше нет элементов, является исключительным условием, и перехват этого исключения за кулисами в цикле for является довольно простым способом реализации "брать элементы, пока не осталось" .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...