NSI, Première • Fiche de révision à Montréal

Fiche de révision : Python, types, contrôle et tableaux (NSI Première)

Cette fiche ne reprend pas le cours de Python : vous l'avez déjà dans vos notes. Elle traite ce que le cours ne dit jamais et qui décide pourtant de la note, à savoir le bug exact qui coûte deux points sur une copie, la boucle à choisir selon ce que l'énoncé demande, et la preuve de terminaison que le correcteur attend.

Elle est écrite pour les élèves de Première qui suivent la spécialité numérique et sciences informatiques du programme français, au lycée en France comme dans le réseau AEFE. Une fois la fiche lue, la série d'exercices corrigés du même chapitre met chaque réflexe à l'épreuve, de la trace d'exécution jusqu'au traitement d'un relevé de températures.

Marque ici la fiche comme lue ou mets-la en favori : un compte gratuit, sans mot de passe, retient tes fiches lues et tes favoris d'une visite à l'autre, te dit quel chapitre attaquer ensuite et te permet de demander la méthode ou l'exercice qui te manque. Crée ton espace, un courriel suffit.

Le fil du chapitre

Python fait exactement ce qu'on écrit, jamais ce qu'on voulait écrire. La moitié des points perdus vient de trois gestes : une borne de droite qu'on croit incluse, deux flottants comparés avec ====, et une affectation qui duplique une référence au lieu de l'objet.

Ce chapitre fait partie de NSI en Première
Pour s'entraîner Les exercices corrigés de ce chapitre, 17 exercices

Avant ce chapitre

Cette fiche suppose ces notions acquises. Si une méthode ci-dessous reste opaque, c'est presque toujours l'une d'elles qui manque, pas la fiche.

Remonter plus loin : la chaîne complète (4 chapitres) ↓

Le chemin de remédiation, du plus ancien au plus proche. Un élève qui reprend ce chapitre de zéro le reprend dans cet ordre.

  1. 1 Algorithmique et Scratch Cinquième, Mathématiques
  2. 2 Algorithmique et Scratch Quatrième, Mathématiques
  3. 3 Algorithmique et Scratch Troisième, Mathématiques
  4. 4 Algorithmique et Python Seconde, Mathématiques

L'essentiel

Les bornes : ce que range et les indices couvrent vraiment

  • • range(a, b) parcourt aa, a+1a+1, ..., b−1b-1 : la borne de droite est EXCLUE. range(n) donne donc 0,1,…,n−10, 1, \ldots, n-1, soit nn valeurs.
  • • Le nombre de valeurs de range(i, j) vaut j−ij-i, ce qui est la seule chose à retenir pour ne pas se tromper d'un cran.
  • • Les indices d'une liste de longueur nn vont de 00 à n−1n-1. Le dernier élément est t[n - 1], ou t[-1], jamais t[n].
  • • Une fonction de bibliothèque ne suit pas forcément la même convention : randrange(1, 30) exclut 3030 comme range, randint(1, 30) l'inclut. Seule la documentation tranche.
abcdefgh01234567indicest = [a, b, c, d, e, f, g, h]range(2, 6) : 2, 3, 4, 5la borne 6 est EXCLUE
L'accolade couvre exactement les quatre cases d'indices 22 à 55. La case 66 est dessinée, elle existe, et pourtant range(2, 6) ne l'atteint pas.

Le réflexe qui évite l'erreur d'un cran : compter j−ij-i éléments plutôt que d'énumérer. range(2, 6) contient 6−2=46-2=4 valeurs, et cela se vérifie sans écrire la liste.

Ce que Python évalue, et dans quel ordre

  • • a / b renvoie TOUJOURS un flottant, même sur deux entiers. a // b est le quotient entier, arrondi vers le BAS, et a % b le reste.
  • • Arrondi vers le bas veut dire vers moins l'infini : −7 // 2-7\ //\ 2 vaut −4-4, pas −3-3. C'est le piège des entiers négatifs.
  • • Ne jamais tester l'égalité de deux flottants avec ==== : on compare avec une tolérance, abs(a - b) < 1e-9.
  • • and et or s'évaluent en COURT-CIRCUIT, de gauche à droite : dans a != 0 and b / a > 1, la division n'est jamais tentée si a vaut 00. L'ordre des tests est donc porteur de sens.
−5−4−3−2−101-7 / 2 = -3.5-7 // 2 = -4 : vers le bas-3 : vers zéro, pas pour //
−7/2-7/2 vaut −3,5-3{,}5 ; le quotient entier descend vers moins l'infini et donne −4-4 (vert). −3-3 (rouge) serait l'arrondi vers zéro, celui de int(), pas celui de //.

De Morgan, à savoir dans les deux sens : not (a and b) équivaut à (not a) or (not b), et not (a or b) équivaut à (not a) and (not b). Le not se distribue en RETOURNANT le connecteur.

Une variable ne contient pas un objet, elle le désigne

  • • Une affectation copie une RÉFÉRENCE. Après b = a sur une liste, les deux noms désignent le même objet : modifier b modifie ce que voit a.
  • • Sur les types immuables, entiers, flottants, chaînes et tuples, la question ne se pose pas : l'objet ne peut pas changer, donc le partage est invisible.
  • • b = list(a), ou b = [x for x in a], fabrique une vraie copie, suffisante pour une liste de nombres. Pour une liste de listes, les sous-listes restent partagées.
  • • Une fonction reçoit elle aussi la référence : elle peut modifier une liste passée en argument, et cette modification survit à la fin de l'appel.
programmetappel f(t)u1234après u.append(4)une seule liste
Le paramètre u de f reçoit la même référence que t : u.append(4) modifie l'unique liste, et t la voit changée après l'appel.

Corollaire à connaître pour l'oral : une fonction qui modifie son argument et qui renvoie aussi une valeur fait deux choses à la fois, ce qu'une spécification claire évite.

Spécifier : préconditions, postconditions et documentation

  • • PRÉCONDITION : ce que l'APPELANT doit garantir sur les arguments. Elle se vérifie à l'entrée, avant tout calcul : assert n >= 0.
  • • POSTCONDITION : ce que la FONCTION garantit sur son résultat. Elle se vérifie juste avant le return : assert r * r <= n < (r + 1) * (r + 1).
  • • Un jeu de tests vérifie des CAS écrits à l'avance ; une postcondition vérifie une PROPRIÉTÉ à chaque appel. Le succès d'un jeu de tests ne garantit pas la correction.
  • • Lire une documentation, c'est y chercher quatre choses : les paramètres et leurs valeurs par défaut, les bornes incluses ou exclues, la valeur renvoyée, les exceptions levées.
r0123456r * r0149162536n = 30 ; r * r <= n : r = 0, 1, 2, 3, 4 ou 5et n < (r + 1) * (r + 1) : r = 5 seulement
Pour n=30n=30, r * r <= n laisse passer r=0r=0 à 55 (orange) ; la seconde moitié, 30<(r+1)230<(r+1)^{2}, ne garde que r=5r=5 (vert) : 25≤30<3625\leq 30<36.

Hors de sa précondition, une fonction ne promet rien : racine_entiere(-4) qui lève une AssertionError n'est pas un bug de la fonction, c'est une faute de l'appel, signalée à l'endroit exact où elle a été commise.

Pour aller plus loin, non exigible : les tranches

  • • Le programme de Première le dit en toutes lettres : « aucune connaissance des tranches (slices) n'est exigible ».
  • • Pour les lire quand on les croise : t[i:j] contient les indices ii à j−1j-1, soit j−ij-i éléments ; t[::-1] renvoie la liste renversée ; t[:] en fabrique une copie.
  • • Au programme, la même extraction s'écrit par une compréhension sur les indices, [t[k] for k in range(i, j)], et la copie par list(t).

Une tranche n'est pas fausse sur une copie ; mais quand l'énoncé demande d'écrire l'algorithme, renverser une chaîne ou comparer ses extrémités, c'est la boucle sur les indices qui est attendue.

Les pièges qui coûtent des points

Les erreurs ci-dessous sont celles que je corrige le plus souvent en séance. Chacune coûte des points sur une copie, même quand le raisonnement est juste.

1. Comparer deux flottants avec un double égal

2 points, et un programme qui ne s'arrête jamais si le test pilote une boucle

Ce qu'il ne faut pas écrire

« if 0.1 + 0.2 == 0.3: ... et le test passe, puisque c'est vrai en mathématiques. »

Ce qu'il faut écrire

« Les flottants sont des approximations binaires : 0.1 + 0.2 vaut 0.30000000000000004, donc le test est FAUX. On écrit if abs(0.1 + 0.2 - 0.3) < 1e-9. »

0,3 exact0.299999999999999930.3 stocké0.300000000000000040.1 + 0.2écart 5,55 × 10⁻¹⁷ : == répond Falseabs(a - b) < 1e-9 : la tolérance couvre tout le dessin
Les flottants voisins de 0,30{,}3 sont espacés de 5,55×10−175{,}55\times 10^{-17} : 0.3 est stocké sur l'un, 0.1 + 0.2 tombe sur le suivant. Une tolérance de 10−910^{-9} les confond.

Pourquoi : Un dixième n'a pas d'écriture binaire finie, exactement comme un tiers n'a pas d'écriture décimale finie. L'erreur est minuscule mais l'égalité, elle, est binaire.

2. Croire la borne de droite incluse

toute la question quand la boucle sort du tableau

Ce qu'il ne faut pas écrire

« for i in range(1, n): parcourt tous les indices de 1 à n, donc tout le tableau sauf le premier. »

Ce qu'il faut écrire

« range(1, n) s'arrête AVANT n : il parcourt 1 à n - 1, ce qui est exactement la bonne plage pour une liste de longueur n. Pour comparer t[i] et t[i+1], il faut range(len(t) - 1). »

4071129334?5range(len(t) - 1) : 4 paires, toutes dans trange(len(t)) : t[4] et t[5] ?t
range(len(t) - 1) forme les quatre paires d'un tableau de cinq cases (vert) ; range(len(t)) en ajoute une cinquième qui lit t[5], hors du tableau (rouge).

Pourquoi : Les indices vont de 00 à n−1n-1 : la borne exclue de range est précisément ce qui fait coïncider les deux conventions. Le comprendre supprime toute l'incertitude d'un cran.

3. Croire qu'une affectation copie la liste

3 points, et un bug introuvable à la lecture

Ce qu'il ne faut pas écrire

« b = a puis b.append(4), et a n'a pas changé puisque j'ai travaillé sur b. »

Ce qu'il faut écrire

« b = a fait désigner le MÊME objet par les deux noms : a vaut aussi [1, 2, 3, 4] après l'append. Pour une vraie copie, il faut b = list(a), ou b = [x for x in a]. »

b = ab = list(a)ab[1, 2, 3]ab[1, 2, 3][1, 2, 3]un seul objetdeux objets
À gauche, une seule liste porte deux étiquettes : ce que fait bb, aa le subit. À droite, list(a) a fabriqué un second objet, et les deux noms deviennent indépendants.

Pourquoi : En Python, une variable est une étiquette posée sur un objet, jamais une boîte qui le contient. Dupliquer l'étiquette ne duplique pas l'objet.

4. Donner une liste comme valeur par défaut d'un paramètre

2 points, et une fonction dont le résultat dépend de l'historique

Ce qu'il ne faut pas écrire

« def ajoute(x, t=[]): t.append(x); return t, et chaque appel repart d'une liste vide. »

Ce qu'il faut écrire

« La valeur par défaut est créée UNE SEULE FOIS, à la définition : la même liste sert à tous les appels et se remplit. On écrit def ajoute(x, t=None): if t is None: t = []. »

t=[] : une seule liste123ajoute(1)ajoute(2)ajoute(3)t=None : une liste par appelajoute(1)1ajoute(2)2ajoute(3)3
Avec t=[], les trois appels remplissent la même liste, créée à la définition (rouge). Avec t=None, chaque appel fabrique la sienne (vert).

Pourquoi : C'est le corollaire direct du piège précédent : la valeur par défaut est un objet, et il est partagé par tous les appels qui ne le remplacent pas.

5. Inverser la ligne et la colonne d'un tableau à deux dimensions

toute la question sur un tableau non carré, où l'erreur provoque un plantage

Ce qu'il ne faut pas écrire

« t[i][j] désigne la colonne i et la ligne j, comme des coordonnées x et y. »

Ce qu'il faut écrire

« t est une liste de LIGNES : t[i] est la ligne numéro i, et t[i][j] la case de colonne j dans cette ligne. len(t) donne le nombre de lignes, len(t[0]) le nombre de colonnes. »

i = 0i = 1i = 2j = 0j = 1j = 2t[1][2]len(t) = lignes, len(t[0]) = colonnest[i][j] : i la LIGNE, j la COLONNE
Le premier crochet descend dans les lignes, le second se déplace dans la ligne choisie. La case marquée est t[1][2] : deuxième ligne, troisième colonne.

Pourquoi : L'ordre vient de l'imbrication : on ouvre d'abord la liste extérieure, donc la ligne. Sur un tableau carré, l'erreur passe inaperçue jusqu'au jour où il ne l'est plus.

6. Afficher au lieu de renvoyer

2 points, et souvent l'exercice entier qui s'effondre après

Ce qu'il ne faut pas écrire

« def carre(x): print(x * x), puis y = carre(3) et j'utilise y ensuite. »

Ce qu'il faut écrire

« print AFFICHE, return RENVOIE. Sans return, la fonction renvoie None, donc y vaut None et la suite plante. On écrit def carre(x): return x * x. »

Pourquoi : Une fonction sert à produire une valeur réutilisable. L'affichage est un effet de bord destiné à l'humain, et il ne transmet rien au programme.

7. Confondre la division et le quotient entier

1 point, et un indice flottant qui plante l'accès au tableau

Ce qu'il ne faut pas écrire

« 7 / 2 vaut 3 puisque les deux nombres sont des entiers. »

Ce qu'il faut écrire

« 7 / 2 vaut 3.5, un flottant, TOUJOURS. Le quotient entier s'écrit 7 // 2, qui vaut 3, et l'arrondi se fait vers le bas : -7 // 2 vaut -4. »

Pourquoi : Un flottant ne peut pas servir d'indice : t[len(t) / 2] lève une erreur, alors que t[len(t) // 2] fonctionne. C'est là que le bug se manifeste, loin de la ligne fautive.

8. Écrire une boucle while sans exhiber son variant

2 points de justification, systématiquement demandés

Ce qu'il ne faut pas écrire

« while n != 1: n = n // 2, la boucle finit forcément par s'arrêter. »

Ce qu'il faut écrire

« Il faut EXHIBER un variant : ici n est un entier strictement positif qui décroît strictement à chaque tour, donc la boucle se termine. Sur un n négatif ou nul, elle ne s'arrête jamais, ce que la précondition doit exclure. »

123456−8−44812162024arrêt : n = 1depuis 20 : 4 toursdepuis -5 : bloqué à -1valeur de n après chaque tour
Tours en abscisse. Depuis 2020 : 1010, 55, 22, 11, arrêt (vert). Depuis −5-5, n reste bloqué à −1-1, car −1 // 2-1\ //\ 2 vaut −1-1 (rouge) : la précondition n > 0 compte.

Pourquoi : « Cela finit bien par s'arrêter » n'est pas une preuve. Le variant est une quantité entière, positive, strictement décroissante : les trois adjectifs sont exigés.

9. Croire qu'un jeu de tests qui passe prouve la fonction

2 points, la question « proposer un jeu de tests » entière

Ce qu'il ne faut pas écrire

« Mes cinq tests passent, racine(0), racine(1), racine(4), racine(9) et racine(16) : ma fonction de racine entière est juste. »

Ce qu'il faut écrire

« Les cinq tests sont des carrés parfaits : ils ne vérifient qu'une famille de cas. Sur 3030, la fonction renvoie 66 alors que 62=36>306^{2}=36>30. J'ajoute un entier non carré au jeu de tests, et la postcondition assert r * r <= n < (r + 1) * (r + 1) avant le return. »

2468101214161820123456racine_camaraderacine_entiereles cinq testsnracine renvoyée
La version du camarade (rouge) ne coïncide avec la racine juste (vert) que sur les carrés 0,1,4,9,160, 1, 4, 9, 16, exactement ses cinq tests ; partout ailleurs elle renvoie un de trop.

Pourquoi : Le programme le dit : le succès d'un jeu de tests ne garantit pas la correction d'un programme. Un test vérifie un CAS, une postcondition vérifie une PROPRIÉTÉ, à chaque appel.

10. Deviner la borne d'une fonction de bibliothèque au lieu de lire sa documentation

1 point, et un programme faux qui tourne sans erreur

Ce qu'il ne faut pas écrire

« randint(1, 30) et randrange(1, 30) tirent tous les deux un numéro d'élève de 1 à 30. »

Ce qu'il faut écrire

« La documentation de randint écrit a <= N <= b : 3030 valeurs, borne comprise. randrange suit range, borne de droite EXCLUE : 2929 valeurs, et l'élève 3030 ne passe jamais. J'écris randint(1, 30) ou randrange(1, 31). »

Pourquoi : Deux fonctions du même module n'ont pas la même convention. Le nom ne dit rien des bornes : seule la documentation le dit, et c'est elle qu'on cite sur la copie.

Quelle méthode choisir

Quelle boucle, d'après ce que l'énoncé demande

On regarde une seule chose : sait-on à l'avance COMBIEN de tours il faudra faire ?

  • Si le nombre de tours est connu à l'avance, ou dépend d'une longueur → boucle bornée for avec range

    Exemple : for i in range(len(t)):

    C'est la boucle par défaut : elle se termine toujours, aucune preuve n'est à écrire.

  • Si on parcourt tous les éléments sans avoir besoin de leur position → for x in t, qui parcourt les VALEURS

    Exemple : for temperature in releve:

    Plus lisible, et impossible de sortir du tableau.

  • Si on a besoin de l'indice, pour modifier une case ou comparer t[i] et t[i+1] → for i in range(len(t)), ou range(len(t) - 1) si l'on regarde le suivant

    Exemple : for i in range(len(t) - 1): if t[i] > t[i+1]:

    Le −1-1 est exactement ce qui évite l'accès hors du tableau au dernier tour.

  • Si on s'arrête sur une condition, sans savoir au bout de combien de tours → boucle while, accompagnée d'un variant à exhiber

    Exemple : while n > 1: n = n // 2

    Une boucle while sans variant écrit est une réponse incomplète, même si le code marche.

Modifier une liste PENDANT qu'on la parcourt fausse le parcours : on construit une nouvelle liste, ou on parcourt une copie faite avec list(t).

Copie ou référence : ce que l'affectation fait vraiment

La question ne se pose que pour les objets MUTABLES, c'est à dire les listes et les dictionnaires. On commence donc par regarder le type.

b = list(a)ab1234b = [list(l) for l in a]ab12341234
list(a) copie la liste extérieure mais pas les lignes : a[0] et b[0] désignent la même sous-liste (rouge). Copier chaque ligne donne deux tableaux indépendants (vert).
  • Si l'objet est un entier, un flottant, une chaîne ou un tuple → aucun risque : ces types sont immuables, on ne peut pas les modifier en place

    Exemple : b = a puis b = b + 1 laisse a inchangé

  • Si l'objet est une liste et qu'on écrit b = a → les deux noms désignent LE MÊME objet, toute modification par l'un se voit par l'autre

    Exemple : b.append(4) ajoute aussi dans a

  • Si l'objet est une liste de nombres et qu'on veut deux listes indépendantes → copie de surface avec b = list(a), ou b = [x for x in a]

    Exemple : b[0] = 99 ne touche plus a

  • Si l'objet est une liste de listes → la copie de surface ne suffit pas : les sous-listes restent partagées

    Exemple : b = [list(ligne) for ligne in a] recopie chaque ligne

    C'est le cas des tableaux à deux dimensions, donc du chapitre suivant.

Le test qui tranche en une ligne : modifier b, puis afficher a. Si a a changé, les deux noms désignaient le même objet.

La rédaction attendue

Le correcteur coche des étapes. Les voici dans l'ordre, avec la phrase de conclusion qu'il attend mot pour mot.

Prouver qu'une boucle while se termine

Quand l'utiliser : Dès qu'une boucle non bornée apparaît, et systématiquement quand l'énoncé écrit « justifier que l'algorithme se termine ».

  1. 1 Nommer la quantité choisie comme variant, en une phrase : « Considérons la valeur de n au début de chaque tour. »
  2. 2 Montrer qu'elle est ENTIÈRE et POSITIVE, en s'appuyant sur la précondition de la fonction.
  3. 3 Montrer qu'elle DÉCROÎT STRICTEMENT à chaque tour, en citant l'instruction responsable.
  4. 4 Conclure : une suite d'entiers positifs strictement décroissante est finie, donc la boucle se termine.

Phrase de conclusion

La valeur de n est un entier strictement positif d'après la précondition, et l'instruction n = n // 2 la fait strictement décroître à chaque tour tant que n est supérieur à 1. Une suite d'entiers positifs strictement décroissante étant finie, la boucle se termine.

Le piège : Se contenter de « la boucle finit par s'arrêter ». Les trois propriétés, entière, positive, strictement décroissante, doivent être écrites une par une : c'est le barème.

Barème : En général 0,50{,}5 point pour le variant nommé, 0,50{,}5 pour la positivité, 11 point pour la décroissance stricte, 0,50{,}5 pour la conclusion.

Spécifier et tester une fonction

Quand l'utiliser : Dès que l'énoncé demande d'écrire une fonction, et toujours avant d'écrire la première ligne de code.

  1. 1 Écrire la docstring d'abord : ce que la fonction reçoit, ce qu'elle renvoie, et la précondition qui doit être vraie à l'appel.
  2. 2 Choisir les noms des paramètres et le TYPE de la valeur renvoyée, puis seulement écrire le corps.
  3. 3 Construire un jeu de tests avec au moins trois cas : un cas courant, un cas limite, et un cas qui a failli être oublié.
  4. 4 Écrire la postcondition en assertion juste avant le return : elle vérifie le résultat à chaque appel, y compris sur les cas qu'aucun test n'a prévus.
  5. 5 Les cas limites obligatoires : la liste vide, la liste à un seul élément, le premier et le dernier indice, et les valeurs négatives.

Phrase de conclusion

La fonction maximum(t) renvoie le couple constitué du plus grand élément de t et de son indice; sa précondition est que t soit une liste non vide de nombres. Testée sur [3, 9, 2, 9, 1], elle renvoie (9, 1), et sur [-5, -2, -9] elle renvoie (-2, 1).

Le piège : Ne tester que sur l'exemple de l'énoncé. Le cas qui fait tomber une copie est presque toujours la liste à un seul élément, ou une liste de valeurs toutes négatives quand le maximum a été initialisé à zéro.

Barème : 11 point pour la spécification, 22 points pour le code, 11 point pour un jeu de tests couvrant un cas limite.

Vérifier avant de rendre

Cinq minutes de vérification récupèrent plus de points qu'un exercice de plus commencé à la hâte.

L'exercice type décortiqué

Le maximum et son indice, spécifié, écrit, puis testé sur ses cas limites

Écrire une fonction maximum(t) qui renvoie le couple formé du plus grand élément de la liste t et de l'indice de sa première occurrence. Justifier le choix de l'initialisation et proposer un jeu de tests.

On testera sur [3, 9, 2, 9, 1], sur [7] et sur [-5, -2, -9].

3091229314liste 170liste 2−50−21−92liste 3
Les trois listes de test : un maximum en double, une liste d'un seul élément, des valeurs toutes négatives.
python
def maximum(t):
    """Renvoie (m, i) : m le plus grand element de t,
    i l'indice de sa premiere occurrence.
    Precondition : t est une liste non vide de nombres."""
    m = t[0]
    imax = 0
    for i in range(1, len(t)):
        if t[i] > m:
            m = t[i]
            imax = i
    return m, imax

Étape 1

Spécification d'abord : la fonction reçoit une liste de nombres, renvoie un couple, et exige que la liste soit NON VIDE. Cette précondition est écrite avant la première ligne de code.

Pourquoi

La précondition n'est pas de la décoration : c'est elle qui autorise l'initialisation par t[0]. Sans elle, la ligne suivante planterait sur une liste vide.

Étape 2

Initialisation par le premier élément : m = t[0] et imax = 0, jamais m = 0.

Pourquoi

Initialiser à 00 est l'erreur classique : sur [-5, -2, -9], la fonction renverrait 00, une valeur qui n'appartient pas à la liste. Le premier élément, lui, en fait toujours partie.

Étape 3

Boucle for i in range(1, len(t)), donc à partir de l'indice 11 et jusqu'à len(t) - 1 inclus.

Pourquoi

On part de 11 parce que l'indice 00 a déjà servi à l'initialisation, et la borne de droite exclue fait s'arrêter la boucle exactement sur le dernier indice valide.

Étape 4

Comparaison stricte if t[i] > m, et mise à jour des DEUX variables ensemble à l'intérieur du if.

Pourquoi

Le supérieur strict garantit qu'on renvoie la PREMIÈRE occurrence en cas d'ex aequo, ce que la spécification promet. Avec un supérieur ou égal, on renverrait la dernière.

Étape 5

return m, imax à la fin, en dehors de la boucle et sans aucun print.

Pourquoi

Placé dans la boucle, le return sortirait au premier tour. Remplacé par un print, il ne transmettrait rien à l'appelant, qui recevrait None.

Étape 6

Jeu de tests : maximum([3, 9, 2, 9, 1]) donne (9, 1), maximum([7]) donne (7, 0), maximum([-5, -2, -9]) donne (-2, 1).

Pourquoi

Les trois cas ne testent pas la même chose : le premier vérifie l'ex aequo, le deuxième la boucle qui ne fait aucun tour, le troisième l'initialisation. Un test qui ne peut pas échouer ne sert à rien.

Conclusion rédigée

La fonction renvoie bien (9, 1) sur [3, 9, 2, 9, 1], (7, 0) sur la liste à un seul élément et (-2, 1) sur la liste de nombres négatifs. L'initialisation par t[0] et la comparaison strictement supérieure sont les deux choix qui rendent ces trois résultats corrects.

L'erreur classique sur cet exercice : Initialiser m à 00 et boucler sur range(len(t)). Sur [-5, -2, -9], la fonction renvoie alors 00 avec l'indice 00 : une valeur absente de la liste, et un indice qui ne la désigne pas.

À savoir par cœur

  • • range(a, b) donne aa à b−1b-1, soit b−ab-a valeurs : la borne de droite est TOUJOURS exclue. randint(a, b), lui, inclut bb, sa documentation le dit.
  • • Les indices d'une liste de longueur nn vont de 00 à n−1n-1; le dernier est t[-1].
  • • a / b renvoie un flottant, a // b le quotient entier arrondi vers le BAS : −7 // 2-7\ //\ 2 vaut −4-4.
  • • Jamais de ==== entre deux flottants : comparer abs(a - b) < 1e-9.
  • • and et or s'évaluent en court-circuit, de gauche à droite : l'ordre des tests protège la division par zéro.
  • • b = a partage l'objet ; b = list(a) le copie. Sur une liste de listes, la copie de surface ne suffit pas.
  • • Terminaison d'un while : exhiber un variant ENTIER, POSITIF et STRICTEMENT DÉCROISSANT. Les trois mots comptent.
  • • Précondition : vérifiée à l'entrée, garantie par l'APPELANT. Postcondition : vérifiée avant le return, garantie par la FONCTION.

Questions fréquentes

Pourquoi range(1, n) ne va-t-il pas jusqu'à n ?

Parce que la borne de droite est exclue en Python, dans range comme dans randrange. range de 1 à n parcourt 1, 2, jusqu'à n moins un, ce qui fait n moins un valeurs. Cette convention est exactement celle qui fait coïncider range avec les indices d'une liste, qui vont de zéro à la longueur moins un. Attention, randint de a à b inclut b.

Pourquoi 0.1 plus 0.2 n'est-il pas égal à 0.3 en Python ?

Parce que les flottants sont codés en binaire et qu'un dixième n'a pas d'écriture binaire finie, comme un tiers n'a pas d'écriture décimale finie. La somme vaut en réalité 0.30000000000000004. On ne teste donc jamais l'égalité de deux flottants : on compare leur écart à une tolérance.

Pourquoi ma liste a-t-elle changé alors que j'ai modifié une autre variable ?

Parce qu'une affectation copie la référence, pas l'objet. Après b égale a, les deux noms désignent la même liste en mémoire, donc tout ajout par b se voit par a. Pour obtenir deux listes indépendantes, il faut écrire b égale list de a, ou construire la copie en compréhension.

Quelle différence entre print et return dans une fonction ?

Print affiche un texte à destination de la personne qui lit l'écran, et ne transmet rien au programme. Return renvoie une valeur que l'appelant peut stocker et réutiliser. Une fonction sans return renvoie None, et toute utilisation de son résultat provoquera une erreur plus loin.

Comment prouver qu'une boucle while se termine en NSI ?

On exhibe un variant : une quantité entière, positive, et qui décroît strictement à chaque tour. On cite l'instruction responsable de la décroissance, puis on conclut qu'une suite d'entiers positifs strictement décroissante est finie. Les trois propriétés doivent être écrites séparément, c'est le barème.

À quoi sert assert dans une fonction Python en NSI ?

Une assertion vérifie une condition pendant l'exécution et arrête le programme avec une AssertionError si elle est fausse. Placée à l'entrée d'une fonction, elle garantit la précondition sur les arguments ; placée juste avant le return, elle garantit la postcondition sur le résultat. Elle vérifie ainsi le contrat à chaque appel, là où un jeu de tests ne vérifie que les cas écrits.

Passer à la pratique

Exercices corrigés : Python : types, contrôle, fonctions et tableaux

Une méthode se prouve sur une copie, pas sur une fiche. La série du même chapitre reprend chacun de ces pièges dans un exercice, avec le corrigé rédigé étape par étape.

  • 17 exercices corrigés
  • 170 points
  • 255 minutes
Faire les exercices
Il manque quelque chose dans cette fiche ? Une méthode, un piège, un exercice pour l'appliquer, une erreur repérée : dis-le-moi, je lis chaque demande.

L'envoi passe par ton espace gratuit : c'est ce qui me permet de te répondre et de te prévenir quand l'exercice est ajouté. Un courriel suffit, pas de mot de passe. Crée ton espace. Pas envie de créer un compte ? Écris-moi directement.

De quoi s'agit-il ?
Je lis chaque demande moi-même, personne d'autre ne la voit. Inutile d'écrire ton nom ou celui de ton école.
Quand ta demande est traitée, tu es prévenu par courriel et dans ton espace.

En envoyant, tu acceptes que je lise ta demande pour y répondre. Elle est gardée avec ton compte, effacée avec lui, et jamais publiée : politique de confidentialité.

Fiche suivante Représentation des données : réels et texte

Ce chapitre resservira dans

Les chapitres qui le réclament en amont, plus tard dans l'année ou dans les années suivantes.

© Ahmed Squalli Houssaini. Fiche publiée sur www.letuteurscientifique.ca/fiches/nsi-python-bases. Libre pour l'usage personnel et en classe ; sa republication ailleurs demande une autorisation écrite (mentions légales).

Voir aussi

Vous cherchez un tuteur de NSI à Montréal ?

Contactez-moi pour une première séance. On reprend les réflexes qui coûtent des points en devoir, de la trace d'exécution jusqu'à la preuve de terminaison d'une boucle, puis on les met à l'épreuve sur du code du niveau réel des évaluations de Première.

Site par Studio Squalli