Maths & NSI

Première

Langages et programmation

Ce chapitre couvre les constructions élémentaires de la programmation : variables, conditions, boucles et fonctions, en Python.

Structures conditionnelles

Pourquoi des conditionnelles ?

Les instructions conditionnelles permettent, selon le résultat (vrai ou faux) d'une expression booléenne, d'exécuter un bloc d'instructions plutôt qu'un autre. C'est un élément fondamental de tout langage de programmation.

Syntaxe en Python

if condition:
    # bloc execute si condition est vraie
    ...
else:
    # bloc execute sinon
    ...

Exemple. Tester si un entier nn est pair, en utilisant le reste de la division par 2 (n % 2) :

def suivant(u):
    """Renvoie le terme suivant dans la suite de Syracuse"""
    if u % 2 == 0:
        v = u // 2
    else:
        v = 3 * u + 1
    return v

Conditions imbriquées et elif

Il est possible d'imbriquer des conditions. Pour éviter trop de blocs imbriqués (peu lisibles), Python propose le mot-clé elif (contraction de else if).

Exemple. Une année est bissextile si elle est divisible par 400, ou si elle est divisible par 4 sans être divisible par 100 :

def bissextile(a):
    if a % 400 == 0:
        resultat = True
    elif a % 100 == 0:
        resultat = False
    elif a % 4 == 0:
        resultat = True
    else:
        resultat = False
    return resultat

Un seul des quatre blocs est exécuté (le premier dont la condition est vérifiée), ce qui permet de vérifier facilement la fonction avec des tests :

assert bissextile(1984)
assert bissextile(2000)
assert not bissextile(1982)
assert not bissextile(2100)
Exercice — Tester une fonction avec assert

Écrire une fonction signe(x) qui renvoie la chaîne "positif" si x>0x > 0, "negatif" si x<0x < 0, et "nul" si x=0x = 0. Écrire ensuite trois instructions assert pour vérifier votre fonction.

Exercice — TP Python 1ère — vitesse moyenne et classification du mode de déplacement

Écris un programme Python qui :

  1. demande à l'utilisateur de saisir une distance parcourue en kilomètres, puis un temps de trajet en heures ;
  2. calcule la vitesse moyenne correspondante (en km/h) ;
  3. affiche cette vitesse moyenne arrondie à deux décimales ;
  4. indique, selon la vitesse moyenne obtenue, le mode de déplacement le plus probable, avec les seuils suivants :
    • à pied si v⩽6v \leqslant 6 km/h ;
    • à vélo si 6<v⩽256 < v \leqslant 25 km/h ;
    • en voiture si v>25v > 25 km/h.
Exercice — TP Python 1ère — multiple de deux autres nombres et mention selon un score

Partie A. Écris un programme Python qui lit trois nombres entiers non nuls saisis par l'utilisateur, et affiche "oui" si l'un des trois est un multiple exact des deux autres à la fois, "non" sinon.

Partie B. Écris un programme Python qui saisit un entier s représentant un score, et qui affiche :

  • "Échec" si s<40s < 40,
  • "Moyen" si 40⩽s<6040 \leqslant s < 60,
  • "Bien" si 60⩽s<8060 \leqslant s < 80,
  • "Excellent" si s⩾80s \geqslant 80.
Exercice — Une calculatrice et un jury d'examen

D'après des fiches d'exercices de NSI Première.

1. Calculatrice. L'utilisateur saisit deux nombres et une opération (+, -, * ou /). Le programme affiche le calcul et son résultat avec quatre décimales, par exemple :

Nombre 1 : 13.2
Opération (+, -, * ou /) : /
Nombre 2 : 3
13.2000 / 3.0000 = 4.4000

Prévoir les cas d'une opération inconnue et d'une division par zéro.

2. Jury d'examen. Un candidat passe trois épreuves. Il est admis :

  • soit s'il a obtenu au moins 9 à chacune des trois épreuves ;
  • soit si la moyenne de ses trois notes est au moins égale à 10 et que sa plus basse note est au moins égale à 8.

Sinon, il est refusé. Écrire un programme qui fait saisir le nom du candidat et ses trois notes, puis affiche la décision. Que décide le jury pour un candidat ayant obtenu 10,5 ; 7 et 12 ? Et pour 8 ; 12 et 11 ?

Exercice — Premiers programmes : division entière, modulo et échange de variables

D'après des séries d'exercices de NSI Première.

  1. Durée. Écrire un programme qui demande un nombre de minutes et l'affiche en heures et minutes : 135 minutes donnent « 2 heures et 15 minutes ».
  2. Parité. Écrire un programme qui demande un entier et affiche s'il est pair ou impair.
  3. Échange. On saisit deux valeurs a et b. Échanger leurs contenus, puis les afficher : avec a = 5 et b = 3, on doit obtenir a = 3 et b = 5. Pourquoi les deux instructions a = b puis b = a ne conviennent-elles pas ?
  4. Température. Convertir en degrés Fahrenheit une température saisie en degrés Celsius, avec F=C×95+32F = C \times \dfrac{9}{5} + 32. Que donnent 37 °C ? −40 °C ?
  5. Âge. Demander son âge à l'utilisateur et afficher son année de naissance, en supposant que l'on est en 2024. Pourquoi le résultat peut-il être faux d'un an ?
Exercice — Années bissextiles, tri de trois nombres et tranches d'impôt

D'après des séries d'exercices de NSI Première.

  1. Année bissextile. Une année est bissextile si elle est divisible par 4 mais pas par 100, sauf si elle est divisible par 400. Écrire une fonction bissextile(annee) qui renvoie un booléen, et la tester sur 2024, 2023, 1900 et 2000.
  2. Trois nombres. Écrire une fonction trier3(a, b, c) qui renvoie les trois nombres rangés du plus petit au plus grand, sans utiliser sorted, sort, min ni max.
  3. Appréciation. Écrire une fonction qui renvoie l'appréciation d'une note sur 20 : « Insuffisant » en dessous de 10, « Assez bien » de 10 inclus à 15 exclu, « Bien » de 15 inclus à 18 exclu, « Excellent » à partir de 18.
  4. Impôt. Un barème indique : pas d'impôt en dessous de 10 000 € de revenu annuel ; 10 % entre 10 000 € et 25 000 € ; 20 % au-delà de 25 000 €.
    • a. On l'interprète d'abord ainsi : on repère la tranche du revenu, puis on applique son taux au revenu entier. Écrire la fonction impot(revenu) correspondante. Calculer l'impôt pour 25 000 € puis pour 25 001 €. Que constate-t-on ?
    • b. Dans un barème progressif, chaque taux ne s'applique qu'à la part du revenu située dans sa tranche. Écrire impot_progressif(revenu) et calculer l'impôt pour 20 000 € et pour 30 000 €.
Exercice — Mini-projet : pierre, feuille, ciseaux

D'après des séries d'exercices de NSI Première.

Au jeu de pierre-feuille-ciseaux, la pierre bat les ciseaux, les ciseaux battent la feuille, et la feuille bat la pierre ; deux coups identiques donnent une égalité.

  1. Écrire une fonction gagnant(coup1, coup2) qui renvoie 1 si le premier coup gagne, 2 si le second gagne, 0 en cas d'égalité. Les coups sont les chaînes "pierre", "feuille" et "ciseaux".
  2. Écrire un programme qui fait jouer l'utilisateur contre l'ordinateur, qui choisit son coup au hasard, en cinq manches, puis affiche le score final.

QCM — Structures conditionnelles

1. En Python, quel mot-clé permet d'enchaîner une nouvelle condition après un if sans imbriquer de bloc ?
2. Combien de blocs au maximum peuvent s'exécuter dans un if/elif/elif/else ?
3. En reprenant la fonction suivant du cours (la suite de Syracuse), que renvoie suivant(17) ?

Boucles bornées et non bornées

Boucle bornée : for

Lorsque le nombre de répétitions est connu à l'avance, on utilise une boucle bornée, la boucle for.

for i in range(n):
    instruction1
    instruction2

Sémantique. La variable i prend successivement les valeurs 0,1,2,…,n−10, 1, 2, \ldots, n-1. Pour chacune de ces valeurs, les instructions du corps de la boucle sont exécutées.

Exemple. Afficher la table de multiplication d'un nombre :

def table(nombre):
    """Affiche la table de multiplication de 'nombre', de 0 a 10."""
    for i in range(11):
        print(i, "x", nombre, "=", i * nombre)

On peut aussi utiliser for pour parcourir une chaîne de caractères, terme à terme :

def nombre_de_voyelles(mot):
    """Renvoie le nombre de voyelles dans 'mot' (suppose en minuscules)."""
    voyelles = "aeiouy"
    compteur = 0
    for lettre in mot:
        if lettre in voyelles:
            compteur = compteur + 1
    return compteur
 
assert nombre_de_voyelles("programmation") == 5

Boucle non bornée : while

Lorsque le nombre de répétitions n'est pas connu à l'avance, mais dépend de la vérification d'une condition, on utilise une boucle non bornée, la boucle while.

while condition:
    instruction1
    instruction2

Sémantique. La condition est évaluée : si elle est vraie, le corps de la boucle est exécuté puis on revient tester la condition ; si elle est fausse, on sort de la boucle.

Attention aux boucles infinies ! Une boucle while correctement construite comporte trois éléments :

  1. une initialisation avant d'entrer dans la boucle ;
  2. un test qui détermine si l'on continue ou si l'on s'arrête ;
  3. une instruction qui modifie, à chaque tour, une variable utilisée dans le test.

Exemple. Un capital placé augmente de 5 % chaque année (il est multiplié par 1,05). On veut savoir au bout de combien d'années il dépasse un certain seuil :

def duree_pour_atteindre(capital_depart, seuil):
    """
    Precondition : capital_depart > 0 et seuil > capital_depart
    Resultat : nombre d'annees necessaires pour que le capital,
    qui augmente de 5 % chaque annee, depasse 'seuil'
    """
    annee = 0                    # 1. initialisation
    capital = capital_depart
    while capital < seuil:       # 2. test
        capital = capital * 1.05 # 3. modification de la variable testee
        annee = annee + 1
    return annee
 
assert duree_pour_atteindre(120, 500) == 30

Quelle boucle choisir ?

SituationBoucle adaptée
On connaît à l'avance le nombre de répétitions (parcourir une liste, une chaîne, un intervalle d'entiers)for
Le nombre de répétitions dépend d'une condition qu'on ne peut pas calculer avant de commencer (attendre une saisie valide, itérer jusqu'à ce qu'un seuil soit dépassé)while

Complément. Dans une boucle, l'instruction break interrompt immédiatement la boucle, et continue passe directement au tour suivant sans exécuter le reste du corps de la boucle.

Exercice — Somme des n premiers entiers : for et while
  1. Écrire une fonction somme_for(n) qui calcule 1+2+⋯+n1 + 2 + \cdots + n à l'aide d'une boucle for.
  2. Écrire une fonction somme_while(n) qui calcule le même résultat à l'aide d'une boucle while.
  3. Vérifier que somme_for(10) et somme_while(10) renvoient bien 5555.
Exercice — TP Python 1ère — somme des N premiers nombres impairs (boucle for et boucle while) et test de tri décroissant

Partie A/B. Écris un programme qui demande à l'utilisateur de saisir un nombre entier positif N, calcule la somme des N premiers nombres impairs (1,3,5,…1, 3, 5, \dots) une première fois à l'aide d'une boucle for i in range(...), puis une seconde fois à l'aide d'une boucle while, et affiche les deux résultats.

Partie C. Écris une fonction est_decroissant(T) qui prend une liste T en entrée et retourne True si ses éléments sont triés en ordre strictement décroissant, False sinon.

Exemples : est_decroissant([9, 7, 5, 3, 1]) doit renvoyer True ; est_decroissant([10, 8, 6, 7, 4]) doit renvoyer False.

Exercice — QCM de devoir : boucles et condition de sortie

D'après un devoir surveillé de NSI Première (2020-2021).

Pour chaque question, une seule proposition est exacte.

Question 1. Quelle valeur affiche ce programme ?

x = 1
for i in range(10):
    x = x * 2
print(x)

A. 1024 — B. 2 — C. 20000000000 — D. 2048

Question 2. On veut afficher tous les nombres pairs de 0 à 152. Par quoi remplacer la ligne en pointillés ?

n = 0
......................
    print(n)
    n = n + 2

A. for i in range(152): — B. for n in range(153): — C. while n <= 152: — D. while n < 151:

Question 3. On considère la fonction suivante, où capital et interet sont des nombres strictement positifs.

def capital_double(capital, interet):
    n = 0
    montant = capital
    while montant <= 2 * capital:
        montant = montant + interet
        n = n + 1
    return n

Que peut-on toujours affirmer à propos du nombre n renvoyé ?

A. capital * n * interet > 2 * capital

B. n == 2 * capital / interet

C. capital + n * interet > 2 * capital

D. n == capital / interet

Exercice — Compléter un test de primalité avec une boucle while

D'après un devoir surveillé de NSI Première (2020-2021).

La fonction suivante doit indiquer si un entier a supérieur à 2 est premier, ou bien afficher l'un de ses diviseurs.

from math import sqrt
 
def premier(a):
    i = 2
    while a % i != 0:
        i = i + 1
        if i > sqrt(a):
            print(...)
            i = a
    if ...:
        print("Le nombre", a, "est divisible par", i)
  1. À quoi sert la boucle while (ligne while a % i != 0:) ?
  2. Compléter l'instruction print(...).
  3. Compléter la condition if ...: de l'avant-dernière ligne.
  4. Dérouler l'appel premier(9), puis l'appel premier(7).
Exercice — Tables de multiplication et triangles d'étoiles

D'après des fiches d'exercices de NSI Première.

1. Écrire un programme qui fait saisir un entier naturel n, puis affiche sa table de multiplication de 0 à 11, sous la forme 0 * 17 = 0, 1 * 17 = 17, etc.

2. Écrire un programme qui réalise l'affichage suivant :

*****
****
***
**
*

3. Écrire un programme qui fait saisir un entier N supérieur ou égal à 2, puis affiche un triangle « vide » de hauteur N. Pour N = 5 :

*
**
* *
*  *
*****
Exercice — Saisie avec valeur sentinelle et analyse d'un texte

D'après des séries d'exercices de NSI Première.

  1. Valeur sentinelle. Écrire un programme qui fait saisir des notes, entre 0 et 20, sans savoir à l'avance combien il y en aura. La saisie s'arrête quand l'utilisateur entre −1 ; une note hors de l'intervalle est refusée. Le programme affiche alors la moyenne des notes saisies. Pourquoi faut-il ici une boucle while ? Quel cas particulier faut-il traiter ?
  2. Analyse d'un texte. Écrire un programme qui fait saisir une phrase, puis affiche son nombre de mots, son nombre de voyelles et son mot le plus long. Que donne-t-il pour « Le petit chat dort sur le canapé » ?
Exercice — Épreuve pratique NSI 2024 — Sujet 12, exercice 2 : jeu du plus ou moins

Banque nationale de sujets 2024 de l'épreuve pratique, sujet n°12, exercice 2.

Le jeu du « plus ou moins » consiste à deviner un nombre entier choisi entre 1 et 99. Une élève de NSI décide de le coder en langage Python de la manière suivante :

  • le programme génère un nombre entier aléatoire compris entre 1 et 99 ;
  • si la proposition de l'utilisatrice est plus petite que le nombre cherché, l'utilisatrice en est avertie ; elle peut alors en tester un autre ;
  • si la proposition de l'utilisatrice est plus grande que le nombre cherché, l'utilisatrice en est avertie ; elle peut alors en tester un autre ;
  • si l'utilisatrice trouve le bon nombre en 10 essais ou moins, elle gagne ;
  • si l'utilisatrice a fait plus de 10 essais sans trouver le bon nombre, elle perd.

La fonction randint est utilisée : si a et b sont des entiers tels que a <= b, randint(a, b) renvoie un nombre entier compris entre a et b inclus.

Compléter le code ci-dessous et le tester :

from random import randint
 
def plus_ou_moins():
    nb_mystere = randint(1, ...)
    nb_test = int(input("Proposez un nombre entre 1 et 99 : "))
    compteur = ...
 
    while nb_mystere != ... and compteur < ...:
        compteur = compteur + 1
        if nb_mystere ... nb_test:
            nb_test = int(input("Trop petit ! Testez encore : "))
        else:
            nb_test = int(input("Trop grand ! Testez encore : "))
 
    if nb_mystere == nb_test:
        print ("Bravo ! Le nombre était ", ...)
        print("Nombre d'essais: ", ...)
    else:
        print ("Perdu ! Le nombre était ", ...)
Correction réservée aux abonnés Premium.

Créez un compte gratuit : votre première correction est offerte.

Exercice — Épreuve pratique NSI 2024 — Sujet 18, exercice 1 : multiplier avec des additions

Banque nationale de sujets 2024 de l'épreuve pratique, sujet n°18, exercice 1.

Programmer la fonction multiplication qui prend en paramètres deux nombres entiers relatifs n1 et n2, et qui renvoie le produit de ces deux nombres.

Les seules opérations arithmétiques autorisées sont l'addition et la soustraction.

Exemples :

>>> multiplication(3, 5)
15
>>> multiplication(-4, -8)
32
>>> multiplication(-2, 6)
-12
>>> multiplication(-2, 0)
0
Correction réservée aux abonnés Premium.

Créez un compte gratuit : votre première correction est offerte.

Exercice — Épreuve pratique NSI 2024 — Sujet 19, exercice 1 : listes de puissances d'un entier

Banque nationale de sujets 2024 de l'épreuve pratique, sujet n°19, exercice 1.

On rappelle que :

  • le nombre ana^n est le nombre a×a×a×⋯×aa \times a \times a \times \cdots \times a, où le facteur aa apparaît nn fois ;
  • en langage Python, l'instruction t[-1] permet d'accéder au dernier élément du tableau t.

Dans cet exercice, l'opérateur ** et la fonction pow ne sont pas autorisés.

Programmer en langage Python une fonction liste_puissances qui prend en argument un nombre entier aa, un entier strictement positif nn et qui renvoie la liste de ses puissances [a1,a2,…,an][a^1, a^2, \ldots, a^n].

Programmer également une fonction liste_puissances_borne qui prend en argument un nombre entier aa supérieur ou égal à 2 et un entier borne, et qui renvoie la liste de ses puissances, à l'exclusion de a0a^0, strictement inférieures à borne.

Exemples :

>>> liste_puissances(3, 5)
[3, 9, 27, 81, 243]
>>> liste_puissances(-2, 4)
[-2, 4, -8, 16]
>>> liste_puissances_borne(2, 16)
[2, 4, 8]
>>> liste_puissances_borne(2, 17)
[2, 4, 8, 16]
>>> liste_puissances_borne(5, 5)
[]
Correction réservée aux abonnés Premium.

Créez un compte gratuit : votre première correction est offerte.

Exercice — Épreuve pratique NSI 2024 — Sujet 26, exercice 2 : simuler un jeu de plateau

Banque nationale de sujets 2024 de l'épreuve pratique, sujet n°26, exercice 2.

On considère une piste carrée qui contient 4 cases par côté. Les cases sont numérotées de 0 inclus à 12 exclu comme ci-dessous :

+----+----+----+----+
|  0 |  1 |  2 |  3 |
+----+----+----+----+
| 11 |         |  4 |
+----+         +----+
| 10 |         |  5 |
+----+----+----+----+
|  9 |  8 |  7 |  6 |
+----+----+----+----+

L'objectif de l'exercice est d'implémenter le jeu suivant :

Au départ, le joueur place son pion sur la case 0. À chaque coup, il lance un dé équilibré à six faces et avance son pion d'autant de cases que le nombre indiqué par le dé (entre 1 et 6 inclus) dans le sens des aiguilles d'une montre.

Par exemple, s'il obtient 2 au premier lancer, il pose son pion sur la case 2 puis s'il obtient 6 au deuxième lancer, il le pose sur la case 8, puis s'il obtient à nouveau 6, il pose le pion sur la case 2.

Le jeu se termine lorsque le joueur a posé son pion sur toutes les cases de la piste.

Compléter la fonction nombre_coups ci-dessous de sorte qu'elle renvoie le nombre de lancers aléatoires nécessaires pour terminer le jeu.

Proposer ensuite quelques tests pour en vérifier le fonctionnement.

from random import randint
 
def nombre_coups():
    '''Simule un jeu de plateau avec 12 cases et renvoie le nombre
    minimal de coups pour visiter toutes les cases.'''
    nombre_cases = 12
    # indique si une case a été vue
    cases_vues = [ False ] * nombre_cases
    nombre_cases_vues = 1
    cases_vues[0] = True
    case_en_cours = 0
    n = ...
    while ... < ...:
        x = randint(1, 6)
        case_en_cours = (case_en_cours + ...) % ...
        if ...:
            cases_vues[case_en_cours] = True
            nombre_cases_vues = ...
        n = ...
    return n
Correction réservée aux abonnés Premium.

Créez un compte gratuit : votre première correction est offerte.

Exercice — Épreuve pratique NSI 2024 — Sujet 33, exercice 1 : renverser une chaîne de caractères

Banque nationale de sujets 2024 de l'épreuve pratique, sujet n°33, exercice 1.

Programmer une fonction renverse, prenant en paramètre une chaîne de caractères non vide mot et renvoie cette chaîne de caractères en ordre inverse.

Exemple :

>>> renverse("")
''
>>> renverse("abc")
'cba'
>>> renverse("informatique")
'euqitamrofni'
Correction réservée aux abonnés Premium.

Créez un compte gratuit : votre première correction est offerte.

Exercice — Épreuve pratique NSI 2024 — Sujet 48, exercice 2 : suite « look and say »

Banque nationale de sujets 2024 de l'épreuve pratique, sujet n°48, exercice 2.

On considère dans cet exercice la suite de nombres suivante : 1, 11, 21, 1211, 111221, …

Cette suite est construite ainsi : pour passer d'une valeur à la suivante, on la lit et on l'écrit sous la forme d'un nombre. Ainsi, pour 1211 :

  • on lit un 1, un 2, deux 1 ;
  • on écrit donc en nombre 1 1, 1 2, 2 1 ;
  • puis on concatène 111221.

Compléter la fonction nombre_suivant qui prend en entrée un nombre sous forme de chaîne de caractères et qui renvoie le nombre suivant par ce procédé, encore sous forme de chaîne de caractères.

def nombre_suivant(s):
    '''Renvoie le nombre suivant de celui representé par s
    en appliquant le procédé de lecture.'''
    resultat = ''
    chiffre = s[0]
    compte = 1
    for i in range(...):
        if s[i] == chiffre:
            compte = ...
        else:
            resultat += ... + ...
            chiffre = ...
            ...
    lecture_... = ... + ...
    resultat += lecture_chiffre
    return resultat

Exemples :

>>> nombre_suivant('1211')
'111221'
>>> nombre_suivant('311')
'1321'
Correction réservée aux abonnés Premium.

Créez un compte gratuit : votre première correction est offerte.

Exercice — Épreuve pratique NSI 2026 — Sujet 17 : budget d'un club de handball

Banque nationale de sujets 2026 de l'épreuve pratique, sujet n°17 (situation d'évaluation d'une heure).

Budget d'un club de handball

Un club de handball professionnel souhaite mieux comprendre et gérer ses dépenses et recettes annuelles. Les dirigeants veulent identifier les postes de dépenses les plus importants et s'assurer de l'exactitude de leur solde budgétaire de fin d'année.

On dispose de jeux de données sous forme de fichiers CSV représentant les mouvements budgétaires du club. Chaque mouvement est décrit par un enregistrement comportant les attributs suivants :

  • 'type' : chaîne de caractères dont les valeurs possibles sont 'dépense' ou 'recette' ;
  • 'catégorie' : chaîne de caractères dont les valeurs possibles sont 'subventions', 'sponsoring', 'billetterie', 'marketing', 'salaires', 'déplacements', 'fonctionnement', 'cotisations' ou 'autres_charges' ;
  • 'montant' : nombre flottant positif représentant la somme en euros ;
  • 'mois' : nombre entier compris entre 1 et 12.

Une fois chargés par le script analyse_budget.py, ces jeux de données donnent une liste de dictionnaires mouvements. Deux jeux de données sont fournis :

  • un jeu de test défini directement dans le code dans la variable mouvements_test ;
  • un jeu complet budget_complet.csv de plus de 2 000 mouvements.

Le fichier analyse_budget.py contient plusieurs fonctions d'analyse que le sujet vise à compléter.

Question 1. Écrire la fonction total_par_type(mouvements, type_mouvement) qui prend en paramètres une liste de mouvements et une chaîne de caractères (parmi 'dépense' ou 'recette'). Cette fonction doit renvoyer la somme totale des montants correspondant à ce type précis. La fonction renverra 0 si aucun mouvement ne correspond. Créer ensuite une fonction test_total() contenant au moins deux assertions pour valider le bon fonctionnement de votre code sur le jeu de données mouvements_test (par exemple, le total des recettes attendu est de 2300.0).

Appel professeur — Appeler le professeur pour lui présenter votre réponse ou en cas de difficulté.

La fonction solde_annuel(mouvements), déjà codée dans le fichier fourni, est censée calculer le bilan financier de l'année en faisant la somme des soldes de chaque mois (recettes totales moins dépenses totales). Si le solde annuel est positif, le club dégage des bénéfices. S'il est négatif, le club est en déficit.

Question 2. Calculer manuellement le solde annuel attendu pour la liste mouvements_test. Écrire ensuite une fonction test_solde_annuel() contenant une assertion qui vérifie que la fonction solde_annuel(mouvements_test) renvoie bien ce résultat théorique. Exécuter ce test.

L'exécution du test précédent lève une erreur : le résultat renvoyé par la fonction solde_annuel ne correspond pas à la réalité comptable.

Question 3. Analyser le code de la fonction solde_annuel pour identifier la source de cette erreur logique, expliquer pourquoi certains mouvements ont été ignorés, puis proposer une correction. Appliquer ensuite votre fonction corrigée sur le fichier budget_complet.csv pour annoncer le véritable solde du club au professeur.

Appel professeur — Appeler le professeur pour lui présenter votre réponse ou en cas de difficulté.

Fichiers fournis

Le dossier comporte une version PDF de l'énoncé, le code source à analyser et compléter analyse_budget.py et le jeu de données complet budget_complet.csv (2 026 mouvements).

analyse_budget.py

import csv
 
 
def lire_mouvements_depuis_csv(nom_fichier_csv):
    """
    Lit les données d'un fichier CSV et les retourne en liste de dictionnaires.
    Les colonnes 'montant' et 'mois' sont converties respectivement en flottant et en entier.
    """
    mouvements_csv = []
    try:
        with open(nom_fichier_csv, mode='r', newline='', encoding='utf-8') as fichier_csv:
            lecteur_csv = csv.DictReader(fichier_csv)
            for ligne in lecteur_csv:
                ligne['montant'] = float(ligne['montant'])
                ligne['mois'] = int(ligne['mois'])
                mouvements_csv.append(ligne)
        return mouvements_csv
    except FileNotFoundError:
        print(f"Erreur : Le fichier '{nom_fichier_csv}' est introuvable.")
        return []
 
 
mouvements_test = [
    {'type': 'recette', 'catégorie': 'cotisations', 'montant': 1200.0, 'mois': 1},
    {'type': 'recette', 'catégorie': 'billetterie', 'montant': 300.0, 'mois': 6},
    {'type': 'dépense', 'catégorie': 'fonctionnement', 'montant': 450.0, 'mois': 6},
    {'type': 'dépense', 'catégorie': 'déplacements', 'montant': 200.0, 'mois': 12},
    {'type': 'dépense', 'catégorie': 'salaires', 'montant': 1500.0, 'mois': 12},
    {'type': 'recette', 'catégorie': 'subventions', 'montant': 800.0, 'mois': 12}
]
 
#############################################################################
# Écrire ci-dessous la fonction total_par_type et ses tests (Question 1)    #
#############################################################################
 
 
#############################################################################
# Fonctions de calcul du solde (Questions 2 et 3)                           #
#############################################################################
 
def solde_mensuel(mouvements, mois):
    """
    Calcule le solde pour un mois donné (recettes - dépenses).
    """
    total_recettes = 0
    total_depenses = 0
 
    for m in mouvements:
        if m['mois'] == mois:
            if m['type'] == 'recette':
                total_recettes += m['montant']
            else:
                total_depenses += m['montant']
 
    return total_recettes - total_depenses
 
 
def solde_annuel(mouvements):
    """
    Calcule le solde annuel en additionnant les soldes de chaque mois.
    """
    total = 0
    # Parcourt les mois de l'année pour cumuler le bilan
    for m in range(1, 12):
        total = total + solde_mensuel(mouvements, m)
 
    return total
 
 
#############################################################################
# Écrire ci-dessous la fonction test_solde_annuel (Question 2)              #
#############################################################################
 
 
#############################################################################
# Programme principal pour analyser le fichier complet                      #
#############################################################################
 
# mouvements_complets = lire_mouvements_depuis_csv("budget_complet.csv")
# print("Le solde annuel sur le fichier complet est de :", solde_annuel(mouvements_complets))

budget_complet.csv (début du fichier)

type,catégorie,montant,mois
dépense,autres_charges,269.87,1
dépense,autres_charges,165.32,1
dépense,autres_charges,320.95,1
dépense,autres_charges,259.87,1
dépense,autres_charges,100.52,1
...
Correction réservée aux abonnés Premium.

Créez un compte gratuit : votre première correction est offerte.

QCM — Boucles bornées et non bornées

1. Quelle boucle utiliser lorsqu'on ne connaît pas à l'avance le nombre de répétitions nécessaires ?
2. Que se passe-t-il si, dans une boucle while, on oublie de modifier la variable testée dans la condition ?
3. Avec la fonction duree_pour_atteindre du cours, que renvoie l'appel duree_pour_atteindre(100, 110) ?
4. Quelle est la différence entre une boucle while et une boucle for ?
5. Quelle instruction interrompt immédiatement une boucle, avant la fin normale de ses tours ?
6. Que produit le programme suivant ? x = 0, puis une boucle while x < 5: dont le corps est : x += 1, puis if x == 3: continue, puis print(x).

Fonctions : définition, paramètres et valeur de retour

Pourquoi définir des fonctions ?

Écrire des fonctions rend un programme plus simple à comprendre, plus facile à tester et plus facile à modifier. Une fonction est utilisée comme une boîte noire : on lui fournit des valeurs (les paramètres) et elle renvoie un résultat.

Définir une fonction en Python

def nom_fonction(parametre1, parametre2):
    """Docstring : ce que fait la fonction."""
    ...
    return resultat
  • Le mot-clé def introduit la définition, suivi du nom de la fonction et de ses paramètres formels entre parenthèses.
  • Le corps de la fonction est délimité par l'indentation (contrairement à d'autres langages qui utilisent des accolades, voir la notion suivante).
  • L'instruction return termine immédiatement l'exécution de la fonction et renvoie la valeur qui la suit.

Exemple. Calculer un prix TTC (toutes taxes comprises) à partir d'un prix HT et d'un taux de TVA, avec des annotations de type (facultatives) :

def prix_ttc(prix_ht: float, taux_tva: float) -> float:
    """Calcule le prix TTC a partir du prix HT et du taux de TVA (en %)."""
    return prix_ht * (1 + taux_tva / 100)
 
print(prix_ttc(200, 5.5))  # 211.0

Paramètres et arguments

Les noms utilisés dans la définition (prix_ht, taux_tva) sont les paramètres formels. Lors de l'appel, les valeurs fournies (200, 5.5) sont les paramètres effectifs (ou arguments).

ttc1 = prix_ttc(200, 5.5)   # ht = 200, tva = 5.5

À noter. Les variables définies à l'intérieur d'une fonction (comme prix_ht) sont locales : elles n'existent que pendant l'exécution de la fonction et ne sont pas accessibles en dehors.

Valeur de retour et fonctions sans retour

Le résultat renvoyé par une fonction peut être réutilisé dans un calcul :

total = prix_ttc(200, 5.5) + prix_ttc(100, 20)  # total vaut 331.0

Une fonction qui n'a pas d'instruction return (ou qui exécute juste return sans valeur) renvoie automatiquement la valeur spéciale None : on parle parfois de procédure quand l'objectif est un effet (comme un affichage) plutôt qu'un résultat.

def afficher_bienvenue(prenom):
    """Affiche un message de bienvenue (ne renvoie rien)."""
    print("Bienvenue,", prenom)
 
resultat = afficher_bienvenue("Sam")
print(resultat)  # None

Une fonction peut en appeler une autre

def mention(note):
    """Renvoie la mention associee a une note sur 20."""
    if note >= 16:
        return "Très bien"
    elif note >= 14:
        return "Bien"
    elif note >= 12:
        return "Assez bien"
    else:
        return "Passable"
 
def bulletin(controle, examen):
    """Calcule une moyenne ponderee (30 % controle, 70 % examen) et sa mention."""
    moyenne = 0.3 * controle + 0.7 * examen
    return moyenne, mention(moyenne)
Exercice — Moyenne pondérée et mention
  1. Écrire une fonction mention(note) qui renvoie la chaîne "Très bien" si note⩾16note \geqslant 16, "Bien" si 14⩽note<1614 \leqslant note < 16, "Assez bien" si 12⩽note<1412 \leqslant note < 14, et "Passable" sinon.
  2. Écrire une fonction note_finale(controle, examen) qui calcule une moyenne pondérée (30%30\% pour le contrôle, 70%70\% pour l'examen) et renvoie à la fois la moyenne et sa mention (en appelant mention).
  3. Tester note_finale(10, 16) : que renvoie-t-elle ?
Exercice — TP Python 1ère — fonction de calcul de la surface d'un carré

Écris une fonction surface_carre(c) qui calcule et retourne la surface d'un carré de côté c (formule Surface=c2\text{Surface} = c^2). Écris ensuite un programme principal qui demande à l'utilisateur de saisir la longueur du côté du carré, appelle la fonction surface_carre() pour calculer la surface, puis affiche le résultat.

Exercice — TP Python 1ère — décomposer et recomposer une liste de points 3D

On travaille avec des coordonnées représentées sous forme de tuples (x, y, z), correspondant à des points en 3D :

points3D = [(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12)]
  1. Écris une fonction decompose(points) qui prend en paramètre une liste de tuples (x, y, z) et renvoie trois listes distinctes X, Y, Z. Exemple attendu : decompose(points3D) renvoie X=[1, 4, 7, 10], Y=[2, 5, 8, 11], Z=[3, 6, 9, 12].
  2. Écris une fonction recompose(X, Y, Z) qui prend ces trois listes et les fusionne pour reconstruire la liste de tuples initiale. Exemple attendu : recompose(X, Y, Z) renvoie [(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12)].
Exercice — Six petites fonctions à écrire et à tester

D'après des séries d'exercices de NSI Première.

Écrire les fonctions suivantes, puis les tester sur des exemples bien choisis.

  1. compter_voyelles(chaine) renvoie le nombre de voyelles de la chaîne, minuscules ou majuscules, accentuées ou non.
  2. est_palindrome(mot) renvoie True si le mot se lit de la même façon dans les deux sens, comme « radar » ou « kayak ».
  3. est_premier(n) renvoie True si l'entier n est premier, c'est-à-dire s'il a exactement deux diviseurs, 1 et lui-même.
  4. mot_le_plus_long(mots) renvoie le mot le plus long d'une liste non vide (le premier rencontré en cas d'égalité).
  5. mot_de_passe_valide(mdp) renvoie True si le mot de passe compte au moins 8 caractères et contient au moins un chiffre.
  6. factorielle(n) renvoie n!=1×2×⋯×nn! = 1 \times 2 \times \dots \times n, avec la convention 0!=10! = 1.
Exercice — Épreuve pratique NSI 2024 — Sujet 35, exercice 2 : nombres palindromes

Banque nationale de sujets 2024 de l'épreuve pratique, sujet n°35, exercice 2.

Un mot palindrome peut se lire de la même façon de gauche à droite ou de droite à gauche : kayak, radar, et non sont des mots palindromes.

De même certains nombres ont des écritures décimales qui sont des palindromes : 33, 121, 345543.

L'objectif de cet exercice est d'obtenir un programme Python permettant de tester si un nombre est un nombre palindrome.

Pour remplir cette tâche, on vous demande de compléter le code des trois fonctions ci-dessous qui s'appuient les unes sur les autres :

  • inverse_chaine : qui renvoie une chaîne de caractères inversée ;
  • est_palindrome : qui teste si une chaîne de caractères est un palindrome ;
  • est_nbre_palindrome : qui teste si un nombre est un palindrome.

Compléter le code des trois fonctions ci-dessous.

def inverse_chaine(chaine):
    '''Retourne la chaine inversée'''
    resultat = ...
    for caractere in chaine:
        resultat = ...
    return resultat
 
def est_palindrome(chaine):
    '''Renvoie un booléen indiquant si la chaine ch
    est un palindrome'''
    inverse = inverse_chaine(chaine)
    return ...
 
def est_nbre_palindrome(nbre):
    '''Renvoie un booléen indiquant si le nombre nbre
    est un palindrome'''
    chaine = ...
    return est_palindrome(chaine)

Exemples :

>>> inverse_chaine('bac')
'cab'
>>> est_palindrome('NSI')
False
>>> est_palindrome('ISN-NSI')
True
>>> est_nbre_palindrome(214312)
False
>>> est_nbre_palindrome(213312)
True
Correction réservée aux abonnés Premium.

Créez un compte gratuit : votre première correction est offerte.

QCM — Fonctions

1. Que renvoie une fonction Python qui ne contient aucune instruction return ?
2. Comment appelle-t-on les valeurs fournies entre parenthèses lors de l'appel d'une fonction, comme 200 et 5.5 dans prix_ttc(200, 5.5) ?
3. Que renvoie l'appel bulletin(12, 15) avec les fonctions du cours ?
4. La fonction f(x, y) renvoie le couple (y, x) si x > y, et le couple (x, y) sinon. Que renvoie l'appel f(6, 8) ?

Diversité des langages de programmation

Un même problème, plusieurs langages

Python n'est qu'un langage de programmation parmi beaucoup d'autres (JavaScript, Scratch, C, Java...). Comparer Python à un autre langage permet de repérer ce qui est commun à (presque) tous les langages de programmation, et ce qui est spécifique à chacun.

Exemple. La fonction table, qui affiche la table de multiplication d'un nombre, écrite en Python puis en JavaScript :

def table(nombre):
    """Affiche la table de multiplication du parametre 'nombre'."""
    for i in range(11):
        print(i * nombre)
/* Affiche la table de multiplication du paramètre 'nombre' */
function table(nombre) {
    var i;
    for (i = 0; i < 11; i++) {
        console.log(i * nombre);
    }
}

Et la même idée en pseudo-code, indépendant de tout langage :

Fonction table(nombre)
    Pour i de 0 à 10
        Afficher i * nombre
    FinPour
FinFonction

Traits communs

Malgré des syntaxes différentes, les trois versions ci-dessus partagent les mêmes constructions élémentaires :

  • une fonction, avec un nom et un paramètre ;
  • une boucle bornée qui répète 11 fois la même opération ;
  • une variable (i / nombre) ;
  • une instruction d'affichage.

Ces notions (variable, condition, boucle, fonction) existent dans la quasi-totalité des langages de programmation, y compris dans les langages à blocs comme Scratch, où une boucle répéter ou un bloc si... alors jouent exactement le même rôle qu'un for ou un if, mais assemblés visuellement plutôt qu'écrits sous forme de texte.

Traits spécifiques à chaque langage

PythonJavaScript
Délimitation d'un blocindentationaccolades { }
Définir une fonctiondef nom(...):function nom(...) { }
Boucle bornéefor i in range(n):for (i = 0; i < n; i++) { }
Afficher un résultatprint(...)console.log(...)
Fin d'instructionretour à la lignesouvent un point-virgule ;
Déclaration de variableaucun mot-clé nécessairevar, let ou const

Ces différences sont purement syntaxiques : elles ne changent pas ce que le programme calcule, seulement la façon de l'écrire. Un autre exemple de trait spécifique : Python utilise un typage dynamique (le type d'une variable n'est jamais déclaré, il est déduit de sa valeur), alors que d'autres langages comme Java exigent de déclarer le type de chaque variable.

Pourquoi c'est utile

Savoir repérer les traits communs permet, une fois qu'on maîtrise un premier langage (ici Python), de comprendre rapidement les grandes lignes d'un programme écrit dans un langage inconnu : on y reconnaît des variables, des conditions, des boucles, des fonctions — seule leur écriture change.

Exercice — Traduire une fonction Python vers un autre langage

On donne la fonction Python suivante :

def est_majeur(age):
    """Renvoie True si age est superieur ou egal a 18, False sinon."""
    if age >= 18:
        return True
    else:
        return False
  1. Traduire cette fonction en JavaScript (mots-clés function, accolades, return).
  2. Traduire cette fonction en pseudo-code.
  3. Lister trois traits communs et deux traits spécifiques entre les trois versions.
Exercice — Traduire une fonction JavaScript vers Python et repérer une boucle non bornée

On donne la fonction JavaScript suivante :

function sommeJusqua(n) {
    var somme = 0;
    var i = 1;
    while (i <= n) {
        somme = somme + i;
        i = i + 1;
    }
    return somme;
}
  1. Que calcule cette fonction ? Dérouler l'exécution de sommeJusqua(4) en indiquant les valeurs de somme et i à chaque tour de boucle, jusqu'à l'arrêt.
  2. Traduire cette fonction en Python (sans mot-clé var, avec les deux-points et l'indentation attendus).
  3. S'agit-il ici d'une boucle bornée ou d'une boucle non bornée ? Justifier en comparant avec la boucle for utilisée dans le cours.
  4. Citer un trait spécifique à JavaScript utilisé dans ce code et absent en Python (parmi ceux vus dans le cours).
Exercice — Identifier les constructions dans un pseudo-code et en Scratch

On donne l'algorithme suivant en pseudo-code, qui affiche si un nombre est pair ou impair :

Fonction parite(nombre)
    Si nombre % 2 == 0 alors
        Afficher "pair"
    Sinon
        Afficher "impair"
    FinSi
FinFonction
  1. Traduire cet algorithme en Python.
  2. Scratch est un langage à blocs (vu dans le cours) : il n'y a pas de code textuel, on assemble des blocs visuels. Décrire, en une phrase pour chacun, les blocs Scratch qui joueraient le même rôle que la structure Si ... alors ... Sinon ... FinSi, que le test nombre % 2 == 0, et que l'instruction Afficher.
  3. À quoi correspond l'opération % (modulo) sur les nombres 7 et 2 ? Sur 8 et 2 ?
  4. Cet algorithme utilise-t-il une boucle ? Justifier en listant les constructions élémentaires réellement présentes, parmi : variable, condition, boucle, fonction, affichage.

QCM — Diversité des langages

1. En JavaScript, qu'est-ce qui délimite le corps d'une boucle ou d'une fonction, à la place de l'indentation en Python ?
2. Parmi ces éléments, lequel est un trait commun à (presque) tous les langages de programmation, quelle que soit leur syntaxe ?
3. D'après le cours, quelle affirmation décrit correctement la différence de typage entre Python et Java ?

Spécification : préconditions et postconditions

Spécifier une fonction avant de l'écrire

Spécifier une fonction, c'est décrire précisément ce qu'elle doit faire avant d'écrire son code : son nom, ses paramètres (avec leur type), ce qu'elle renvoie, et sous quelles conditions elle fonctionne correctement. On parle du prototype d'une fonction et de son contrat.

Un contrat de fonction comporte deux parties :

  • la précondition : ce que l'on suppose vrai sur les arguments avant l'appel (par exemple, un entier doit être positif) ;
  • la postcondition : ce que l'on garantit être vrai sur le résultat après l'exécution, si la précondition était respectée.

Prototyper une fonction en Python

On documente le contrat directement dans la docstring de la fonction :

def racine_carree_entiere(n: int) -> int:
    """
    Precondition : n >= 0
    Postcondition : le resultat r verifie r * r <= n < (r + 1) * (r + 1)
    """
    ...

Ce prototype suffit à comprendre comment utiliser la fonction, sans même connaître son implémentation : on sait quel type d'argument fournir, et ce que l'on peut attendre du résultat.

Vérifier une précondition avec assert

Une fois le contrat écrit, on implémente la fonction. L'instruction assert permet de vérifier qu'une précondition est bien respectée ; si la condition est fausse, le programme s'arrête immédiatement avec une erreur (AssertionError), ce qui évite de calculer un résultat incohérent.

def racine_carree_entiere(n: int) -> int:
    """
    Precondition : n >= 0
    Postcondition : le resultat r verifie r * r <= n < (r + 1) * (r + 1)
    """
    assert n >= 0, "n doit etre positif ou nul"
    r = 0
    while (r + 1) * (r + 1) <= n:
        r = r + 1
    return r
 
assert racine_carree_entiere(10) == 3   # 3*3=9 <= 10 < 16=4*4
assert racine_carree_entiere(16) == 4   # 4*4=16 <= 16 < 25=5*5

Ici, les deux derniers assert ne vérifient pas la précondition mais servent à tester que la postcondition est bien vérifiée sur des exemples : c'est le sujet de la notion suivante.

Pourquoi spécifier avant de coder ?

  • Cela force à réfléchir au problème avant de se lancer dans l'écriture du code.
  • Cela permet à quelqu'un d'autre (ou à soi-même plus tard) d'utiliser la fonction sans en lire le code.
  • Cela donne une base pour écrire des tests : on sait déjà ce que le résultat doit vérifier.
Exercice — Spécifier puis implémenter indice_du_maximum

On veut écrire une fonction qui renvoie l'indice de l'élément maximum d'une liste de nombres.

  1. Écrire le prototype de la fonction indice_du_maximum(tab), avec sa précondition et sa postcondition, sous forme de docstring.
  2. Implémenter la fonction en Python, en utilisant assert pour vérifier la précondition.
  3. Vérifier avec assert que indice_du_maximum([3, 7, 2, 7, 1]) renvoie bien 1 (le premier indice où le maximum 77 apparaît).
Exercice — Déduire un contrat à partir d'une fonction déjà écrite

On donne la fonction Python suivante, déjà implémentée mais sans aucune documentation :

def moyenne(notes):
    total = 0
    for note in notes:
        total = total + note
    return total / len(notes)
  1. Que se passe-t-il si on appelle moyenne([]) (liste vide) ? Détailler précisément quelle opération pose problème, et quelle erreur Python génère.
  2. En déduire une précondition raisonnable sur le paramètre notes.
  3. Écrire une postcondition qui décrit ce que renvoie la fonction (on pourra utiliser le vocabulaire « moyenne arithmétique »).
  4. Réécrire la fonction avec sa docstring (précondition et postcondition) et un assert qui vérifie la précondition en tout début de fonction.
  5. Avec la précondition ajoutée, que se passe-t-il désormais si on appelle moyenne([]) ?
Exercice — Spécifier et implémenter une division euclidienne

On veut écrire une fonction division_entiere(a, b) qui calcule le quotient q et le reste r de la division euclidienne de a par b, pour a un entier positif ou nul et b un entier strictement positif. Elle doit renvoyer le couple (q, r).

  1. Rappeler la propriété mathématique qui définit le quotient et le reste d'une division euclidienne de a par b (relation entre a, b, q et r, et encadrement de r).
  2. Écrire la précondition de division_entiere(a, b) (deux conditions, une sur a et une sur b).
  3. Écrire la postcondition, en utilisant la propriété rappelée à la question 1.
  4. Implémenter la fonction en Python sans utiliser les opérateurs // et % (on utilisera une boucle qui soustrait b autant de fois que possible), avec un assert qui vérifie la précondition.
  5. Dérouler l'exécution de division_entiere(17, 5) : donner la valeur de q et r à chaque tour de boucle, jusqu'à l'arrêt.
Exercice — Épreuve pratique NSI 2024 — Sujet 37, exercice 1 : moyenne et cas du tableau vide

Banque nationale de sujets 2024 de l'épreuve pratique, sujet n°37, exercice 1.

Programmer la fonction moyenne prenant en paramètre un tableau d'entiers tab (de type list) qui renvoie la moyenne de ses éléments si le tableau est non vide. Proposer une façon de traiter le cas où le tableau passé en paramètre est vide.

Dans cet exercice, on s'interdira d'utiliser la fonction Python sum.

Exemples :

>>> moyenne([5,3,8])
5.333333333333333
>>> moyenne([1,2,3,4,5,6,7,8,9,10])
5.5
>>> moyenne([])
# Comportement différent suivant le traitement proposé.
Correction réservée aux abonnés Premium.

Créez un compte gratuit : votre première correction est offerte.

QCM — Spécification

1. Que décrit la postcondition du contrat d'une fonction ?
2. Que se passe-t-il quand une instruction assert condition est exécutée alors que condition vaut False ?
3. Dans le code du cours, quelle différence y a-t-il entre assert n >= 0, ... (à l'intérieur de la fonction) et assert racine_carree_entiere(10) == 3 (après la définition) ?

Mise au point : jeux de tests et bibliothèques

Construire un jeu de tests

Mettre au point un programme, c'est vérifier qu'il fonctionne correctement, notamment à l'aide d'un jeu de tests : un ensemble de cas, chacun vérifié par une instruction assert, qui compare le résultat obtenu au résultat attendu.

Un bon jeu de tests doit être représentatif : il doit couvrir non seulement des cas "normaux", mais aussi des cas limites (valeur minimale, liste vide, zéro, valeur négative...), qui sont souvent la source des erreurs de programmation.

Exemple. Tester une fonction qui détermine si un entier est premier :

def est_premier(n):
    """
    Precondition : n est un entier >= 2
    Postcondition : renvoie True si n est premier, False sinon
    """
    assert n >= 2
    for diviseur in range(2, n):
        if n % diviseur == 0:
            return False
    return True
 
# Jeu de tests
assert est_premier(2) == True    # cas limite : le plus petit nombre premier
assert est_premier(4) == False   # nombre pair, compose "evident"
assert est_premier(9) == False   # carre d'un nombre impair (cas piege : 9 = 3*3)
assert est_premier(17) == True   # nombre premier "ordinaire"

Le cas est_premier(9) est important : un diviseur impair comme 33 est plus facile à "oublier" en écrivant une fonction que les diviseurs pairs. Un jeu de tests exhaustif sur un petit intervalle (par exemple tester tous les entiers de 22 à 2020) permet d'être encore plus confiant dans le résultat.

Utiliser une bibliothèque standard

Plutôt que de tout réécrire soi-même, on peut utiliser des fonctions déjà écrites et regroupées dans des bibliothèques (ou modules). On importe un module avec le mot-clé import, puis on accède à ses fonctions avec la notation module.fonction(...).

Exemple avec le module math :

import math
 
print(math.sqrt(16))      # 4.0  (racine carree)
print(math.floor(4.7))    # 4    (partie entiere par defaut)
print(math.pi)            # 3.141592653589793
 
help(math.sqrt)           # affiche la documentation de la fonction sqrt

Exemple avec le module random :

import random
 
de = random.randint(1, 6)                              # entier aleatoire entre 1 et 6 inclus
tirage = random.random()                                # flottant aleatoire dans [0, 1[
carte = random.choice(["pique", "coeur", "carreau", "trefle"])  # element au hasard

Lire la documentation. Pour utiliser une fonction d'une bibliothèque, il n'est pas nécessaire de connaître son code : il suffit de lire sa documentation, qui décrit son rôle, ses paramètres et sa valeur de retour — exactement comme le prototype d'une fonction vu dans la notion précédente. On peut consulter la documentation officielle (docs.python.org) ou l'appeler directement en Python avec help(nom_fonction).

Tester une fonction qui utilise le hasard

Une fonction qui repose sur random ne renvoie pas toujours le même résultat : on ne peut donc pas tester une valeur exacte avec ==. On teste plutôt que le résultat respecte les propriétés attendues (type, intervalle...), éventuellement en répétant le test plusieurs fois :

def lancer_de():
    """Simule un lance de de a 6 faces : renvoie un entier entre 1 et 6."""
    return random.randint(1, 6)
 
for _ in range(1000):
    resultat = lancer_de()
    assert 1 <= resultat <= 6   # on teste l'intervalle, pas une valeur precise
Exercice — Jeu de tests et simulation d'un dé
  1. On donne la fonction est_pair(n) suivante :
def est_pair(n):
    """Renvoie True si n est pair, False sinon."""
    return n % 2 == 0

Écrire un jeu de tests représentatif pour cette fonction (au moins 4 cas, en incluant des cas limites : zéro, un nombre négatif).

  1. En utilisant le module random, écrire une fonction lancer_de() qui simule le lancer d'un dé à 6 faces (renvoie un entier entre 1 et 6).

  2. Écrire un test adapté à cette fonction (on ne peut pas tester une valeur exacte).

Exercice — Piéger un bug grâce à un jeu de tests représentatif

On donne la fonction suivante :

def est_positif(n):
    """Renvoie True si n est positif ou nul, False sinon."""
    return n > 0
  1. D'après la docstring, que doit renvoyer est_positif(0) ?
  2. Écrire un jeu de tests représentatif pour cette fonction (au moins 4 cas, en incluant les cas limites n = 0 et un nombre négatif).
  3. Exécuter mentalement chacun de vos tests. L'un d'eux échoue-t-il ? Si oui, lequel, et pourquoi ?
  4. Corriger la fonction pour qu'elle respecte sa docstring.
Exercice — Tester une fonction combinant hasard et bibliothèque math

On veut écrire une fonction distance_a_lorigine() qui tire au hasard un point de coordonnées entières (x, y), avec x et y compris entre -10 et 10 inclus, à l'aide du module random, puis renvoie la distance de ce point à l'origine, calculée avec le module math.

  1. Rappeler la formule mathématique de la distance d'un point (x,y)(x, y) à l'origine.
  2. Écrire la fonction distance_a_lorigine(), en utilisant random.randint pour tirer x et y, et une fonction du module math pour calculer la distance.
  3. Pourquoi ne peut-on pas tester cette fonction avec un simple assert distance_a_lorigine() == valeur_attendue ?
  4. Écrire un test adapté à cette fonction, portant sur des propriétés que le résultat doit toujours vérifier (on pourra s'aider de math.sqrt(200), qui donne la distance maximale possible).
  5. Le cours présente une autre fonction du module random, différente de randint. Laquelle, et quelle différence avec randint ?
Exercice — Corriger les erreurs d'un jeu de devinette

D'après un devoir surveillé de NSI Première (2020-2021).

Le programme suivant doit faire deviner au joueur un entier choisi au hasard entre 0 et 100 inclus. Il contient quatre erreurs : trois l'empêchent de fonctionner, la quatrième lui fait afficher un message faux. Les trouver et les corriger.

from random import randint
 
def devine():
    """Jeu : deviner un entier choisi au hasard entre 0 et 100 inclus."""
    nbOrdi = random.randint(0, 100)
    nbJoueur = -1
    gagne = False
    # On redemande un nombre tant que la partie n'est pas gagnée
    while gagne = False:
        nbJoueur = int(input("Choisissez un nombre entre 0 et 100 : "))
        if nbJoueur = nbOrdi:
            print("Bravo, vous avez trouvé !")
            gagne = True
        elif nbJoueur < nbOrdi:
            print("Trop petit")
    else:
        print("Trop grand")
Exercice — Épreuve pratique NSI 2024 — Sujet 31, exercice 1 : multiplier avec des additions, et tester

Banque nationale de sujets 2024 de l'épreuve pratique, sujet n°31, exercice 1.

Programmer la fonction multiplication, prenant en paramètres deux nombres entiers relatifs n1 et n2, et qui renvoie le produit de ces deux nombres.

Les seules opérations autorisées sont l'addition et la soustraction.

>>> multiplication(3, 5)
15
>>> multiplication(-4, -8)
32
>>> multiplication(-2, 6)
-12
>>> multiplication(-2, 0)
0
Correction réservée aux abonnés Premium.

Créez un compte gratuit : votre première correction est offerte.

Exercice — Épreuve pratique NSI 2026 — Sujet 18 : températures océaniques en Polynésie

Banque nationale de sujets 2026 de l'épreuve pratique, sujet n°18 (situation d'évaluation d'une heure).

Températures océaniques en Polynésie

En Polynésie française, un réseau de bouées océanographiques mesure quotidiennement la température de surface de l'océan dans quatre archipels : Société, Tuamotu, Marquises et Australes. Ces données permettent de documenter le réchauffement climatique dans le Pacifique Sud. Vous êtes chargé(e) de développer des outils d'analyse pour traiter ces relevés et détecter les anomalies thermiques.

On vous fournit le fichier analyse_temperatures_polynesie.py dans lequel figurent les données à utiliser, ainsi que les fonctions à écrire, compléter ou corriger.

Question 1. Écrire une fonction temperature_moyenne(zone, donnees) qui :

  • prend en paramètres une chaîne zone (nom d'un archipel) et un tableau donnees de dictionnaires représentant les relevés ;
  • renvoie la température moyenne (float) pour cette zone ;
  • renvoie None si la zone n'a aucun relevé.

Exemple :

>>> donnees = [
    {'date': '2020-01-15', 'zone': 'Societe', 'temperature': 28.5},
    {'date': '2020-01-16', 'zone': 'Societe', 'temperature': 29.0},
    {'date': '2020-01-15', 'zone': 'Tuamotu', 'temperature': 27.5}
]
>>> temperature_moyenne('Societe', donnees)
28.75
>>> temperature_moyenne('Marquises', donnees)
>>>

Appel professeur — Appeler le professeur pour lui présenter votre réponse ou en cas de difficulté.

Question 2. Écrire une fonction detecter_anomalies(zone, seuil, donnees) qui :

  • prend en paramètres une zone, un seuil (float) et la liste donnees ;
  • calcule la température moyenne de la zone ;
  • renvoie la liste des dates où la température s'écarte de plus de seuil degrés (en valeur absolue, via la fonction abs()) de cette moyenne ;
  • renvoie une liste vide si la zone n'existe pas.

Exemple avec la liste donnees_test fournie dans le script :

>>> detecter_anomalies('Societe', 1.0, donnees_test)
['2010-06-20', '2021-05-30']

Appel professeur — Appeler le professeur pour lui présenter votre réponse ou en cas de difficulté.

Dans le fichier analyse_temperatures_polynesie.py figure le code de la fonction evolution_par_decennie(zone, donnees) censée calculer l'évolution des températures moyennes par décennie (2010, 2020).

Question 3. Compléter les trois fonctions de test, situées à la fin du fichier, pour la fonction evolution_par_decennie en utilisant le jeu de données fourni :

  1. test_zone_inexistante() : tester une zone qui n'existe pas ;
  2. test_une_seule_decennie() : tester une zone avec données sur une seule décennie ;
  3. test_plusieurs_decennies() : tester une zone avec données sur plusieurs décennies.

Vos tests doivent permettre d'identifier le bug présent dans le code.

Question 4. Après avoir identifié le bug grâce à vos tests, corriger la fonction evolution_par_decennie.

Appel professeur — Appeler le professeur pour lui présenter votre réponse ou en cas de difficulté.

Fichier fourni : analyse_temperatures_polynesie.py

# =================================================================================#
# Données de test
donnees_test = [
    # Société - Données sur 2010 et 2020
    {'date': '2010-01-15', 'zone': 'Societe', 'temperature': 27.0},
    {'date': '2010-06-20', 'zone': 'Societe', 'temperature': 26.5},
    {'date': '2011-03-10', 'zone': 'Societe', 'temperature': 27.5},
    {'date': '2020-02-14', 'zone': 'Societe', 'temperature': 28.0},
    {'date': '2020-08-22', 'zone': 'Societe', 'temperature': 28.5},
    {'date': '2021-05-30', 'zone': 'Societe', 'temperature': 29.0},
 
    # Tuamotu - Données sur 2010 et 2020
    {'date': '2015-04-10', 'zone': 'Tuamotu', 'temperature': 26.8},
    {'date': '2020-07-15', 'zone': 'Tuamotu', 'temperature': 27.5},
    {'date': '2021-09-20', 'zone': 'Tuamotu', 'temperature': 28.0},
 
    # Marquises - Données uniquement sur 2020
    {'date': '2020-03-15', 'zone': 'Marquises', 'temperature': 25.5},
    {'date': '2021-07-10', 'zone': 'Marquises', 'temperature': 26.0},
    {'date': '2022-11-25', 'zone': 'Marquises', 'temperature': 26.5},
]
 
# =================================================================================#
#  Question 1 : Ecrire le code de votre fonction température_moyenne
 
 
# =================================================================================#
#  Question 2 : Ecrire le code de votre fonction detection_anomalies
 
 
# =================================================================================#
# code de la fonction evolution_par_decennie à corriger dans la question 4:
 
 
def evolution_par_decennie(zone, donnees):
    """
    Calcule l'évolution des températures moyennes par décennie pour une zone.
 
    ATTENTION: Cette fonction contient un bug volontaire à détecter et corriger.
 
    Arguments:
        zone (str): Nom de l'archipel (ex: 'Societe', 'Tuamotu')
        donnees (list): Liste de dictionnaires de relevés
 
    Renvoie:
        dict: Dictionnaire {décennie : température_moyenne}
              ex: {2010: 27.5, 2020: 28.3}
              Renvoie un dictionnaire vide si la zone n'existe pas
    """
    # Filtrage des relevés pour la zone
    releves_zone = [r for r in donnees if r['zone'] == zone]
 
    if len(releves_zone) == 0:
        return {}
 
    # Regroupement par décennie
    temperatures_par_decennie = {}
 
    for releve in releves_zone:
        # Extraction de l'année de la date (format: 'YYYY-MM-DD')
        annee = int(releve['date'].split('-')[0])
 
        # Calcul de la décennie
        decennie = (annee // 10)
 
        if decennie not in temperatures_par_decennie:
            temperatures_par_decennie[decennie] = []
 
        temperatures_par_decennie[decennie].append(releve['temperature'])
 
    # Calcul des moyennes
    moyennes = {}
    for decennie, temperatures in temperatures_par_decennie.items():
        moyennes[decennie] = round(sum(temperatures) / len(temperatures), 2)
 
    return moyennes
 
 
# =================================================================================#
#  Exercice 2.1 :
"""
Tests
À compléter par le candidat dans le cadre de la question 3
"""
 
 
def test_zone_inexistante():
    """
    Test 1 : Tester une zone qui n'existe pas
 
    À compléter:
    1. Appeler evolution_par_decennie avec une zone inexistante
    2. Vérifier que le résultat est un dictionnaire vide
    """
    pass  # à remplacer par le code du candidat
 
 
def test_une_seule_decennie():
    """
    Test 2: Tester une zone avec données sur une seule décennie
 
    À compléter:
    1. Appeler evolution_par_decennie avec la zone appropriée
    2. Vérifier que le résultat ne contient qu'une seule décennie (2020)
    3. Vérifier la température moyenne
    """
    pass  # à remplacer par le code du candidat
 
 
def test_plusieurs_decennies():
    """
    Test 3 : Tester une zone avec données sur plusieurs décennies
 
    À compléter:
    1. Appeler evolution_par_decennie avec la zone appropriée
    2. Vérifier que le résultat contient bien les clés 2010 et 2020
    3. Vérifier que les températures moyennes sont cohérentes
    """
    pass  # à remplacer par le code du candidat
Correction réservée aux abonnés Premium.

Créez un compte gratuit : votre première correction est offerte.

QCM — Jeux de tests et bibliothèques

1. Pourquoi est-il important d'inclure des cas limites (zéro, liste vide, valeur négative...) dans un jeu de tests ?
2. Quelle instruction permet d'utiliser les fonctions du module math dans un programme Python ?
3. Pourquoi le jeu de tests de lancer_de() (qui simule un dé à 6 faces avec random.randint(1, 6)) utilise-t-il assert 1 <= resultat <= 6 plutôt que, par exemple, assert resultat == 4 ?
4. La documentation du module random indique : randrange(a, b) renvoie un entier N tel que a ≤ N < b. Après import random, quelle expression simule le lancer d'un dé à six faces ?

Exercices bilan

Afficher le niveau de charge d'une batterie

ApplicationCorrigé gratuit

1. Lire une fonction. On considère la fonction suivante :

def niveau_batterie(pourcentage):
    """Renvoie le niveau de charge ('critique', 'faible', 'normale' ou 'pleine')
    pour un pourcentage de batterie compris entre 0 et 100."""
    if pourcentage <= 10:
        niveau = "critique"
    elif pourcentage <= 30:
        niveau = "faible"
    elif pourcentage <= 80:
        niveau = "normale"
    else:
        niveau = "pleine"
    return niveau

Donner la valeur renvoyée par niveau_batterie(45) et par niveau_batterie(10), en précisant pour chacun des deux appels quelle est la première condition évaluée à True dans la chaîne if/elif/else.

2. Cas limites. Écrire un jeu de tests (sous forme d'instructions assert) qui vérifie le comportement de niveau_batterie sur les huit valeurs suivantes : 00, 1010, 1111, 3030, 3131, 8080, 8181, 100100.

3. Une fonction imbriquée. On veut maintenant tenir compte du fait que l'appareil est ou non en train de charger. Écrire une fonction message_batterie(pourcentage, en_charge) qui :

  • appelle niveau_batterie(pourcentage) pour obtenir le niveau,
  • renvoie ce niveau suivi de " (en charge)" si en_charge vaut True,
  • renvoie ce niveau seul sinon.

Vérifier que message_batterie(5, True) renvoie "critique (en charge)".

4. Un piège classique. Un camarade a écrit la version suivante, où les conditions sont rangées dans l'autre sens :

def niveau_batterie_bug(pourcentage):
    if pourcentage <= 80:
        niveau = "normale"
    elif pourcentage <= 30:
        niveau = "faible"
    elif pourcentage <= 10:
        niveau = "critique"
    else:
        niveau = "pleine"
    return niveau

Que renvoie niveau_batterie_bug(5) ? Expliquer précisément pourquoi ce réarrangement des conditions produit un résultat incorrect, et énoncer la règle à respecter pour ranger correctement des conditions elif qui se chevauchent.

Choisir entre boucle for et boucle while

Application

1. for ou while ? Pour chacune des quatre situations suivantes, indiquer si une boucle for ou une boucle while est la mieux adaptée, et justifier en une phrase.

a. Parcourir chacune des notes d'une liste pour en calculer la somme. b. Demander un mot de passe à l'utilisateur, encore et encore, jusqu'à ce qu'il soit correct. c. Afficher les 15 premiers multiples de 3. d. Faire croître un capital, année après année, jusqu'à ce qu'il dépasse un seuil fixé.

2. Un rebond de balle. Une balle est lâchée d'une hauteur de 100 cm. À chaque rebond, elle perd la moitié de sa hauteur (on ne conserve que la partie entière, avec la division entière //). On veut savoir combien de rebonds sont nécessaires pour que la hauteur (entière) atteigne 0.

a. Écrire une fonction nombre_de_rebonds(hauteur) qui renvoie ce nombre de rebonds, en utilisant une boucle while. On précisera dans la docstring la précondition sur hauteur et ce que représente la valeur renvoyée. b. Dérouler à la main l'exécution de nombre_de_rebonds(100), en donnant à chaque tour de boucle la valeur de hauteur et celle du compteur de rebonds. c. Que renvoie nombre_de_rebonds(0) ? Vérifier que la boucle while de votre fonction se comporte correctement dans ce cas particulier, sans qu'il soit nécessaire d'ajouter un test séparé.

3. Repérer les trois ingrédients d'une boucle while. Le cours rappelle qu'une boucle while correctement construite comporte une initialisation, un test, et une instruction qui modifie une variable du test. Identifier ces trois éléments dans la fonction écrite à la question 2.

Correction réservée aux abonnés Premium.

Créez un compte gratuit : votre première correction est offerte.

Paramètres, valeur de retour et fonctions imbriquées

EntraînementCorrigé gratuit

On programme le calcul des frais de livraison d'un site de vente en ligne :

def frais_livraison(poids_kg: float, express: bool) -> float:
    """Calcule les frais de livraison a partir du poids (en kg) et du mode
    de livraison (express ou standard)."""
    base = 2 + 0.5 * poids_kg
    if express:
        return base + 5
    else:
        return base

1. Paramètres formels et effectifs. Dans la définition de frais_livraison, quels sont les paramètres formels ? Pour l'appel frais_livraison(3, True), quels sont les paramètres effectifs ? Calculer la valeur renvoyée par cet appel, en détaillant le calcul de base.

2. Composer des fonctions. On ajoute une deuxième fonction :

def total_commande(prix_produits: float, poids_kg: float, express: bool) -> float:
    """Calcule le total d'une commande : prix des produits + frais de livraison."""
    return prix_produits + frais_livraison(poids_kg, express)

Calculer total_commande(50, 3, True), en indiquant précisément à quel moment la fonction frais_livraison est appelée et quelle valeur elle renvoie à total_commande.

3. Une fonction qui ne renvoie rien. On ajoute une troisième fonction :

def afficher_recu(prix_produits, poids_kg, express):
    """Affiche le montant total de la commande."""
    total = total_commande(prix_produits, poids_kg, express)
    print("Total a payer :", total, "euros")

On exécute ensuite :

resultat = afficher_recu(50, 3, True)
print(resultat)

Qu'affiche exactement ce code, dans l'ordre ? On justifiera en particulier la valeur affichée par le second print.

4. Portée des variables. La variable base, définie à l'intérieur de frais_livraison, est-elle accessible juste après, dans une instruction print(base) placée en dehors de toute fonction ? Justifier en employant le vocabulaire du cours.

Traduire un algorithme entre Python, pseudo-code et JavaScript

Entraînement

On considère la fonction Python suivante :

def somme_pairs(tableau):
    """Renvoie la somme des elements pairs du tableau."""
    somme = 0
    for element in tableau:
        if element % 2 == 0:
            somme = somme + element
    return somme

1. Dérouler l'exécution. Dérouler somme_pairs([1, 2, 3, 4, 5, 6]) en donnant, pour chaque valeur de element, si elle est retenue ou non et la valeur de somme après son passage dans la boucle.

2. Vers le pseudo-code. Traduire somme_pairs en pseudo-code, en reprenant exactement la syntaxe utilisée en cours (mots-clés Fonction... FinFonction, Pour... FinPour, Si... Alors... FinSi, affectation notée <-).

3. Vers JavaScript. Traduire somme_pairs en JavaScript, en respectant les conventions de syntaxe vues en cours pour ce langage (déclaration de variable avec var, bloc délimité par des accolades, boucle for indicée, console.log pour afficher).

4. Trois différences. En comparant les trois versions, citer trois différences purement syntaxiques entre Python et JavaScript (parmi celles données par le tableau du cours ou observées ici), en précisant à chaque fois qu'elles ne changent rien à ce que le programme calcule.

Correction réservée aux abonnés Premium.

Créez un compte gratuit : votre première correction est offerte.

Écrire le contrat d'une fonction : préconditions et postconditions

Entraînement

On veut écrire une fonction premier_diviseur(n) qui, étant donné un entier n supérieur ou égal à 2, renvoie le plus petit diviseur de n supérieur ou égal à 2 (ce diviseur existe toujours, puisque n se divise lui-même).

1. Spécifier avant de coder. Écrire, sous forme de docstring, le contrat de cette fonction (précondition et postcondition), avant même d'en écrire le code, en suivant exactement la présentation du cours.

2. Raisonner sur le contrat seul. Sans écrire une seule ligne de code, et en utilisant uniquement l'énoncé du contrat, donner la valeur que doit renvoyer premier_diviseur(21), puis celle que doit renvoyer premier_diviseur(29) (on rappelle que 29 est un nombre premier).

3. Implémenter et vérifier la précondition. Écrire le code de premier_diviseur, en utilisant une boucle while, et en faisant vérifier la précondition par une instruction assert munie d'un message d'erreur explicite.

4. Que se passe-t-il si la précondition n'est pas respectée ? On considère la fonction de la question 3, mais sans l'instruction assert de vérification de précondition.

  • Décrire précisément ce qui se produit lors de l'appel premier_diviseur(1).
  • Décrire précisément ce qui se produit lors de l'appel premier_diviseur(0).
  • Lequel des deux comportements est le plus dangereux, et pourquoi ?
Correction réservée aux abonnés Premium.

Créez un compte gratuit : votre première correction est offerte.

Concevoir un jeu de tests et utiliser une bibliothèque standard

Entraînement

Un camarade a écrit la fonction suivante, censée déterminer si une chaîne de caractères est un palindrome (elle se lit de la même façon à l'endroit et à l'envers) :

def est_palindrome_v2(mot):
    """Renvoie True si mot se lit de la meme facon a l'endroit et a l'envers."""
    n = len(mot)
    for i in range(n // 2 - 1):
        if mot[i] != mot[n - i - 1]:
            return False
    return True

1. Dérouler l'exécution. Dérouler est_palindrome_v2("abca") : donner la valeur de n, l'ensemble des valeurs prises par i, et la valeur finalement renvoyée.

2. Le mot "abca" est-il un palindrome ? Vérifier à la main, en comparant "abca" à son écriture à l'envers, si c'est bien un palindrome. Que peut-on en conclure sur la fonction est_palindrome_v2 ?

3. Localiser et corriger le bug. Expliquer précisément l'erreur commise dans la borne de range(...), puis proposer une version corrigée est_palindrome(mot).

4. Un jeu de tests représentatif. Écrire un jeu de tests (assert) pour la fonction corrigée, incluant au moins : la chaîne vide, un mot d'une seule lettre, un palindrome de longueur paire, un non-palindrome de longueur paire (on pourra réutiliser "abca"), un palindrome de longueur impaire, et un non-palindrome de longueur impaire.

5. Bibliothèques.

a. Quelle instruction, tapée dans l'interpréteur Python, permet d'afficher la documentation de la fonction est_palindrome sans quitter Python ni consulter internet ?

b. Le module math fournit la fonction math.floor, qui renvoie la partie entière par défaut d'un nombre. Montrer que, pour un entier n positif ou nul, math.floor(n / 2) et n // 2 renvoient toujours le même résultat.

c. La fonction est_palindrome est-elle une fonction qui utilise le hasard, comme la fonction lancer_de vue en cours ? Pourquoi le jeu de tests de la question 4, qui compare directement à des valeurs exactes, est-il suffisant ici, alors qu'il ne le serait pas pour tester lancer_de ?

Correction réservée aux abonnés Premium.

Créez un compte gratuit : votre première correction est offerte.

Sujet type bac : gérer les réservations d'une salle de cinéma

Type bac

Une salle de cinéma comporte un nombre total de places fixé à l'avance. On souhaite programmer la gestion des réservations.

Partie A — Places disponibles.

Écrire une fonction places_disponibles(total, reservees) qui renvoie le nombre de places encore libres. On précisera dans la docstring la précondition (reservees ne peut ni être négatif, ni dépasser total) et on la fera vérifier par une instruction assert.

Partie B — Une demande peut-elle être satisfaite ?

Écrire une fonction peut_reserver(total, reservees, demande) qui renvoie True si demande places supplémentaires peuvent encore être réservées, False sinon. Cette fonction devra appeler places_disponibles.

Partie C — Traiter une liste de demandes.

On reçoit, dans l'ordre, une liste de demandes de réservation (chaque demande est un entier, le nombre de places souhaitées). On veut traiter ces demandes une par une : si une demande peut être satisfaite compte tenu des places déjà réservées, on l'accepte et on met à jour le nombre de places réservées ; sinon, on la refuse et on passe à la suivante.

  1. Écrire une fonction traiter_demandes(total, demandes) qui renvoie un couple (reservees, honorees), où reservees est le nombre total de places finalement réservées et honorees le nombre de demandes qui ont pu être satisfaites.
  2. Dérouler traiter_demandes(10, [3, 4, 5, 2]) demande par demande, en indiquant à chaque étape le nombre de places disponibles, si la demande est acceptée ou refusée, et les valeurs de reservees et honorees après traitement de la demande.
  3. Écrire un jeu de tests (assert) pour traiter_demandes, incluant au moins : une liste de demandes vide, et une demande dont la taille est exactement égale au nombre de places disponibles au moment où elle est traitée.

Partie D — Cohérence des préconditions.

Expliquer pourquoi l'appel à places_disponibles(total, reservees) réalisé à l'intérieur de peut_reserver, puis à l'intérieur de traiter_demandes, ne déclenche jamais l'erreur de l'assert écrit en partie A, quelle que soit la liste demandes fournie.

Correction réservée aux abonnés Premium.

Créez un compte gratuit : votre première correction est offerte.

Chapitre suivant