Lever des exceptions

Effectué

Maintenant que vous avez une bonne compréhension des tracebacks et de la gestion des exceptions, examinons le déclenchement des exceptions.

Vous connaissez peut-être déjà une situation qui peut provoquer une condition d’erreur quand vous écrivez le code. Dans ces situations, il est utile de lever des exceptions qui permettent au code de savoir ce qu’est le problème.

Le déclenchement d’exceptions peut également faciliter la prise de décision pour d’autres parties du code. Comme nous l’avons vu précédemment, en fonction de l’erreur, le code peut prendre des décisions pertinentes pour résoudre, contourner ou ignorer un problème.

Les astronautes limitent leur utilisation de l’eau à environ 11 litres par jour. Créons une fonction qui, en fonction du nombre d’astronautes, peut calculer la quantité d’eau restante après un ou plusieurs jours :

def water_left(astronauts, water_left, days_left):
    daily_usage = astronauts * 11
    total_usage = daily_usage * days_left
    total_water_left = water_left - total_usage
    return f"Total water left after {days_left} days is: {total_water_left} liters"

Essayez avec cinq astronautes, 100 litres d’eau restante et deux jours de consommation :

water_left(5, 100, 2)
'Total water left after 2 days is: -10 liters'

Ce n’est pas très utile, car un déficit en litres serait une erreur. Ensuite, le système de navigation pourrait alerter les astronautes qu’il n’y aura pas suffisamment d’eau pour tout le monde dans deux jours. Si vous êtes ingénieur chargé de la programmation du système de navigation, vous pouvez lever une exception dans la fonction water_left() pour alerter de la condition d’erreur :

def water_left(astronauts, water_left, days_left):
    daily_usage = astronauts * 11
    total_usage = daily_usage * days_left
    total_water_left = water_left - total_usage
    if total_water_left < 0:
        raise RuntimeError(f"There is not enough water for {astronauts} astronauts after {days_left} days!")
    return f"Total water left after {days_left} days is: {total_water_left} liters"

Réexécutez-le :

water_left(5, 100, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in water_left
RuntimeError: There is not enough water for 5 astronauts after 2 days!

Dans le système de navigation, le code destiné à signaler l’alerte peut maintenant utiliser RuntimeError pour alerter :

try:
    water_left(5, 100, 2)
except RuntimeError as err:
    alert_navigation_system(err)

La fonction water_left() peut aussi être mise à jour pour empêcher de passer des types non pris en charge. Essayez de passer des arguments qui ne sont pas des entiers pour vérifier la sortie de l’erreur :

water_left("3", "200", None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in water_left
TypeError: can't multiply sequence by non-int of type 'NoneType'

L’erreur de TypeError n’est pas très conviviale dans le contexte de ce que la fonction attend. Mettez à jour la fonction pour qu’elle utilise TypeError, mais avec un meilleur message :

def water_left(astronauts, water_left, days_left):
    for argument in [astronauts, water_left, days_left]:
        try:
            # If argument is an int, the following operation will work
            argument / 10
        except TypeError:
            # TypeError will be raised only if it isn't the right type 
            # Raise the same exception but with a better error message
            raise TypeError(f"All arguments must be of type int, but received: '{argument}'")
    daily_usage = astronauts * 11
    total_usage = daily_usage * days_left
    total_water_left = water_left - total_usage
    if total_water_left < 0:
        raise RuntimeError(f"There is not enough water for {astronauts} astronauts after {days_left} days!")
    return f"Total water left after {days_left} days is: {total_water_left} liters"

Réessayez maintenant pour obtenir une meilleure erreur :

water_left("3", "200", None)
Traceback (most recent call last):
  File "<stdin>", line 5, in water_left
TypeError: unsupported operand type(s) for /: 'str' and 'int'

During handling of the preceding exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 9, in water_left
TypeError: All arguments must be of type int, but received: '3'