Question exceptions + signal de fin de l'itérateur: pourquoi est-ce mauvais en Java et normal en Python?


Je suis vraiment confus: L'approche standard en Java est de lancer des exceptions uniquement dans des conditions "anormales" et de ne pas les utiliser pour signaler la fin de l'itérateur.

exemples: Java effectif, poste 57 ("Utiliser les exceptions uniquement pour des conditions exceptionnelles") et JavaSpecialists newsletter 162:

Contrôle de flux

Nous ne devrions jamais provoquer une exception qui serait autrement évitable. J'ai vu du code où au lieu de vérifier les limites, il est supposé que les données seront correctes et que RuntimeExceptions sont interceptées:

Voici un exemple de code incorrect (veuillez ne pas coder comme ceci):

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
    }
  }
}

alors qu'il est standard pour utiliser cet idiome en Python, par ex. StopIteration:

exception StopIteration

Relevé par la méthode next () de l’itérateur pour signaler qu’il n’ya pas d’autres valeurs. Ceci est dérivé de Exception plutôt que StandardError, car cela n'est pas considéré comme une erreur dans son application normale.

Pourquoi est-ce mauvais pour Java mais bon pour Python?


11
2017-10-17 21:10


origine


Réponses:


Python et Java ont des approches très différentes des exceptions. En Python, les exceptions sont normales. Chercher EAFP (Plus facile de demander pardon que la permission) dans le glossaire Python. Vérifiez également ce que Wikipédia a dire

StopIteration est juste un exemple d'EAFP - allez-y et obtenez la prochaine chose de l'itérateur, et si cela échoue, gérez l'erreur.

Si le code est plus lisible avec une sortie non locale, en Python, vous utilisez une exception. Vous n'écrivez pas de chèques, vous ne faites que gérer les échecs si les choses ne fonctionnent pas. Il n'y a absolument rien de honteux, en fait, c'est encouragé. Contrairement à Java.


Maintenant, pour un cas spécifique de StopIteration: Considérez fonctions du générateur.

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

Pour soutenir une sorte de has_next() méthode, le générateur devrait vérifier la valeur suivante, déclenchant la print avant le 2 est demandé La valeur (ou exception levée) devrait être mémorisée dans l'itérateur. Si has_next a été appelé deux fois, seul le premier déclencherait l'effet secondaire. Ou la valeur suivante pourrait toujours être précalculée, même si elle n'est pas nécessaire.

Je trouve la sémantique de Python - le calcul uniquement lorsque la valeur suivante est nécessaire - la plus belle alternative.

Bien sûr, Java ne dispose pas de générateurs de reprise, il est donc difficile de comparer ici. Mais c'est une preuve anecdotique que StopIteration généralise mieux que hasNext ().


6
2017-10-17 21:40



Il n'y a rien que s'arrête vous utilisez des exceptions comme celle-ci dans java, ça a juste l'air moche, du moins pour un développeur java.

La raison principale est que les empilements à partir d’exceptions sont coûteux, et peut-être aussi que les développeurs java pourraient être légèrement plus concernés à propos de dépenser des ressources informatiques que les développeurs Python.

Java est aussi un langage plutôt "propre" - certains diraient fondamentaliste, qui est l'une des raisons pour lesquelles c'est une belle langue. (* voir commentaire)

En tous cas. Les fondamentalistes (et quelques personnes normales) pensent que l'utilisation des exceptions pour un flux normal n'est tout simplement pas la bonne façon ... :-)

Mais à côté de cela, les récents jvm détectent que vous générez beaucoup de stacktraces pour le même endroit du code, et jettera des exceptions sans eux "après un certain temps" pour accélérer les choses.


2
2017-10-17 22:18



Java parfois le faitaussi: "Toutes les implémentations de DataInput méthodes utilisent EOFException au lieu de retourner les valeurs. "Dans ce cas, il est impossible d’utiliser la valeur sentinelle habituelle, -1.


1
2017-10-17 21:22



La raison pour laquelle il n'est pas recommandé est que dans Java, la gestion des exceptions est généralement coûteuse à traiter et à récupérer. Lorsqu'une exception est lancée, le JVM revient en arrière pour fournir une trace de pile qui n'est jamais une bonne chose pour les performances. En bref, il s’agit d’une mauvaise utilisation du langage - il y aura généralement un moyen plus propre et plus efficace de gérer la logique. Considérez le code suivant:

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...");

}

Une meilleure approche consiste à utiliser simplement la logique de contrôle de java:

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

Comme vous pouvez le constater en comparant ces deux extraits de code, la gestion des exceptions est un peu ridicule. Une meilleure approche pour l'exemple que vous avez posté serait quelque chose comme ceci:

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

Les exceptions sont mieux utilisées lorsque les choses tournent mal, comme IOException ("pas d'espace disponible sur l'appareil"), ClassNotFoundException ("impossible de trouver votre code à exécuter") ou NoRouteToHostException ("Impossible de se connecter à l'hôte").


1
2017-10-17 22:27



StopIteration existe en Python pour une itération simple sur n'importe quelle séquence.

L'utilisation d'une exception pour implémenter cela était un choix de conception, et ce n'est vraiment pas incompatible avec la mentalité d'exception de Java. Un itérateur next() la méthode étant appelée quand il n'y a plus d'éléments est une condition exceptionnelle, et attrapant cette exception dans les coulisses d'un for loop est un moyen assez simple d'implémenter "les éléments à prendre jusqu'à ce qu'il n'en reste plus".


0
2017-10-17 21:28



Il n'y a pas de bonne ou de mauvaise réponse à cela. La gestion des exceptions est une structure de flux de contrôle neutre, dont la meilleure utilisation dépend du contexte et du style.

Dans ce cas, Java et Python font la même chose, pour les mêmes raisons: java.util.Iterator's next() les usages NoSuchElementException pour signaler la fin de l'itérateur. C'est simplement un bon style, dans les deux langues: l'alternative d'utiliser une valeur de retour sentinelle spéciale est bien pire pour diverses raisons.

En règle générale, vous devriez envisager d'utiliser des exceptions chaque fois que vous écrivez une fonction qui veut signaler plus d'un type de retour de flux de contrôle. Cela inclut des conditions d'erreur anormales, mais le bon usage de la signalisation des exceptions ne se limite certainement pas à cela.


0
2017-10-17 22:02



Les exceptions dans Java capturent la pile d'exécution, qui est extrêmement lente: Quelle est la lenteur des exceptions Java?

Si vous utilisez une exception dans un flux de contrôle null comme Throwable. Sinon, le code Java standard sera appelé via la hiérarchie de super appels:

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
2017-07-11 09:44