Cours de Python / Calculer un bilan de notes avec validation et moyenne

Un bilan de notes combine validation des données, classement et calcul d’agrégats. Il faut exclure les notes invalides de la moyenne tout en affichant leur nombre, afin que le lecteur puisse distinguer un résultat calculé sur toutes les données d’un résultat après exclusions.

Définir les règles avant de calculer

L’exercice part d’une liste de nombres entiers déjà préparée. Une note valide appartient à l’intervalle de zéro à cent inclus ; une note d’au moins soixante est une réussite. Une valeur négative ou supérieure à cent est comptée comme invalide. La conversion de chaînes provenant d’un formulaire ou d’un fichier CSV reste une étape distincte.

Cas Traitement attendu
60 Note valide et réussite
0 Note valide, sans réussite
-1 ou 101 Exclusion et incrément du compteur d’erreurs
Aucune note valide Message d’absence de données, sans moyenne

Ces règles reposent sur les comparaisons et les conditions booléennes. La documentation Python du contrôle de flux explique les branches et les boucles utilisées dans ce programme.

Exécuter le bilan complet des notes

Python 3 et sa bibliothèque standard suffisent. Enregistrez le bloc complet dans scores.py, au format UTF-8, dans un dossier d’exercice. Avec l’environnement virtuel du projet activé, ouvrez le terminal dans ce dossier puis lancez python scores.py. Sur macOS ou Ubuntu hors environnement virtuel, utilisez python3 scores.py si votre interpréteur se nomme python3.

scores = [80, 60, 0, 100, -1, 101]
valid = []
invalid = 0
passed = 0
for score in scores:
    if not 0 <= score <= 100:
        invalid += 1
        continue
    valid.append(score)
    if score >= 60:
        passed += 1
print("valid:", len(valid), "invalid:", invalid)
if valid:
    print(f"average: {sum(valid) / len(valid):.1f}")
    print("passed:", passed)
else:
    print("no valid scores")

Le résultat attendu est :

valid: 4 invalid: 2
average: 60.0
passed: 3

Quatre notes sont conservées : 80, 60, 0 et 100. Leur somme est 240 ; divisée par quatre, elle donne 60. La mise en forme :.1f affiche une décimale, d’où 60.0. Les notes 80, 60 et 100 atteignent le seuil de réussite, ce qui explique passed: 3. Les deux autres valeurs de départ sont comptées dans invalid.

Suivre les compteurs et le dénominateur

valid contient les notes retenues, invalid compte les valeurs hors intervalle et passed compte les réussites parmi les notes retenues. Lorsqu’un score échoue au test d’intervalle, le compteur d’erreurs augmente puis continue saute l’ajout à valid et le test de réussite. Une note de 101 ne doit donc pas être comptée comme réussite sous prétexte qu’elle dépasse soixante.

La moyenne utilise len(valid) comme dénominateur, jamais la longueur de la liste initiale après avoir retiré des valeurs de la somme. Avec les données initiales, diviser 240 par les six entrées donnerait 40, ce qui ne serait pas la moyenne des notes valides. Vérifiez aussi que len(valid) + invalid == len(scores) : toute entrée doit être comptabilisée une fois.

Le zéro représente une note valide et doit rester dans la moyenne. Le filtrer avec un simple if score l’écarterait à tort, car sa valeur de vérité est fausse. À la fin, if valid: teste en revanche si la liste contient au moins un élément. Une liste contenant seulement zéro est non vide : sa moyenne doit bien être calculée et vaut zéro.

Le calcul intervient après la collecte des notes, à l’intérieur de cette vérification de liste non vide. Si aucune note n’est valide, le programme affiche no valid scores, qui signifie « aucune note valide », et ne divise pas par zéro. Il n’affiche pas non plus de ligne passed dans ce cas. Une moyenne absente et une moyenne égale à zéro restent deux résultats distincts.

Vérifier les frontières et les hypothèses de type

  1. Remplacez uniquement la liste initiale par []. Le programme affiche valid: 0 invalid: 0, puis no valid scores, sans moyenne.
  2. Essayez [59, 60, 61]. Vous devez obtenir trois notes valides, aucune exclusion, une moyenne de 60.0 et deux réussites. Si >= 60 était remplacé par > 60, il n’y aurait plus qu’une réussite : restaurez ensuite le test inclusif.
  3. Essayez [-1, 101]. Les deux valeurs sont exclues ; aucune moyenne ne doit être affichée. Essayez séparément [0] : une note reste valide, la moyenne vaut 0.0 et aucune note ne réussit.
  4. Restaurez [80, 60, 0, 100, -1, 101]. Retrouvez les trois lignes de référence et vérifiez que les quatre notes valides et les deux exclusions couvrent les six entrées.

Ce programme vérifie un intervalle sous l’hypothèse d’une liste d’entiers ; son test ne prouve pas le type des éléments. Un nombre comme 60.5 passerait le contrôle de plage, tandis qu’une chaîne comme "60" provoquerait une TypeError dans la comparaison. De plus, bool est une sous-classe de int : True peut être traité comme un et False comme zéro. La documentation du type bool précise cette relation.

Si l’entrée réelle doit accepter exclusivement les entiers natifs et rejeter les booléens, prévoyez un contrôle de type explicite avant le test d’intervalle, par exemple type(score) is int. Si vous autorisez des décimaux ou d’autres types numériques, définissez plutôt leurs règles de validation et de classement. Le contrat de données doit évoluer avec la source, sans supposer que le seul contrôle de plage suffit.