Leçon 03-1 — Listes : stocker en ordre
1. Objectif
À la fin de cette leçon tu sauras : créer une liste, lire et modifier ses éléments par index (positif et négatif), ajouter (append, insert) et retirer (remove, pop) des éléments, trier (sort / sorted), tester la présence d'une valeur avec in, et lire un IndexError sans paniquer.
2. Pourquoi c'est important
Jusqu'ici tes programmes manipulaient quelques variables : un score, un prix, un compteur. Les vrais programmes manipulent des quantités de données : les 200 lignes d'un relevé bancaire, les tâches d'une todo-list, les mots d'un texte. La liste est LA structure de base pour ça — c'est l'objet le plus utilisé de tout Python. Chaque niveau suivant (fonctions, fichiers, POO, APIs) te fera lire et produire des listes en permanence.
3. Explication simple
Une liste est une rangée de casiers numérotés, dans l'ordre, à partir de 0.
scores = [12, 7, 19]
index : 0 1 2
┌────┬────┬────┐
scores → │ 12 │ 7 │ 19 │
└────┴────┴────┘
scores[0]ouvre le casier 0 →12. Le premier casier est le numéro 0, pas le 1 — commerange(3)commence à 0 (⏪ niveau 02).len(scores)compte les casiers →3. Le dernier casier porte donc le numérolen(scores) - 1, ici2.- Une liste se modifie : on peut changer un casier (
scores[1] = 8), en ajouter au bout (scores.append(15)), en retirer (scores.remove(7)).
C'est tout : une liste, c'est de l'ordre + des numéros + le droit de modifier.
4. Explication approfondie
a) Les index négatifs comptent depuis la fin. scores[-1] est le dernier élément, scores[-2] l'avant-dernier. C'est du vrai Python idiomatique, pas une astuce : scores[-1] est plus lisible et plus sûr que scores[len(scores) - 1].
index positif : 0 1 2
index négatif : -3 -2 -1
┌────┬────┬────┐
│ 12 │ 7 │ 19 │
└────┴────┴────┘
b) Deux familles d'opérations — LA distinction du niveau. Certaines méthodes modifient la liste sur place et renvoient None : append, insert, remove, sort, pop (qui, lui, renvoie l'élément retiré). D'autres fabriquent une nouvelle valeur sans toucher la liste : sorted(), len(), sum(). Confondre les deux produit le bug classique lst = lst.sort() (section 6). À chaque méthode que tu apprends, pose-toi LA question : elle modifie, ou elle renvoie ?
c) Une liste peut tout contenir — mais ne devrait pas. [1, "deux", 3.0] est légal. En pratique, une bonne liste contient des éléments du même type : une liste de scores, une liste de noms. Les mélanges rendent le tri impossible (TypeError, section 7) et le code illisible. Quand tu voudras grouper des choses de types différents (un nom ET un âge), le tuple et le dictionnaire (⏩ leçon 03-3) seront les bons outils.
d) Une liste est un objet mutable. « Mutable » = modifiable après création. C'est sa force… et la source du piège le plus important du niveau : deux variables peuvent pointer vers la même liste, et la modifier par l'une « change » l'autre. ⏩ C'est toute la leçon 03-4 — la plus difficile du niveau. Pour l'instant, retiens juste : une liste peut être modifiée, un nombre ou un texte non.
Cette leçon-ci est surtout de la mécanique : beaucoup de méthodes à pratiquer, rien de conceptuellement dur. Compte 3–4 jours avec les exercices.
5. Exemples commentés
Exemple 1 — créer, lire, mesurer
scores = [12, 7, 19]
print(scores[0]) # 12 : premier casier
print(scores[2]) # 19 : troisième casier (index 2 !)
print(scores[-1]) # 19 : dernier, compté depuis la fin
print(len(scores)) # 3 : nombre d'éléments
print(scores) # [12, 7, 19] : print affiche la liste entière
Exemple 2 — modifier un casier existant
scores = [12, 7, 19]
scores[1] = 8 # on REMPLACE le contenu du casier 1
print(scores) # [12, 8, 19]
Exemple 3 — ajouter : append (au bout) et insert (où tu veux)
scores = [12, 8, 19]
scores.append(15) # ajoute AU BOUT
print(scores) # [12, 8, 19, 15]
scores.insert(0, 3) # insère 3 À l'index 0 ; tout le reste se décale
print(scores) # [3, 12, 8, 19, 15]
Exemple 4 — retirer : remove (par valeur) et pop (par index)
scores = [3, 12, 8, 19, 15]
scores.remove(19) # retire la VALEUR 19 (la première rencontrée)
print(scores) # [3, 12, 8, 15]
last = scores.pop() # retire ET RENVOIE le dernier élément
print(last, scores) # 15 [3, 12, 8]
first = scores.pop(0) # pop(index) : retire à un index précis
print(first, scores) # 3 [12, 8]
remove travaille par valeur, pop par index — et pop te rend l'élément, ce qui permet de l'utiliser.
Exemple 5 — trier : sort (modifie) contre sorted (renvoie)
nums = [3, 1, 2]
print(sorted(nums)) # [1, 2, 3] : une NOUVELLE liste triée...
print(nums) # [3, 1, 2] : ...l'originale n'a pas bougé
nums.sort() # trie SUR PLACE, ne renvoie rien d'utile
print(nums) # [1, 2, 3]
nums.sort(reverse=True)
print(nums) # [3, 2, 1] : ordre décroissant
words = ["poire", "Abricot", "banane"]
words.sort()
print(words) # ['Abricot', 'banane', 'poire'] — les MAJUSCULES
# passent avant les minuscules (ordre du code des
# caractères, pas ordre du dictionnaire)
Exemple 6 — chercher : in, count, index ; et parcourir
scores = [12, 8, 19, 8]
print(8 in scores) # True : la valeur est quelque part dans la liste
print(99 in scores) # False
print(scores.count(8)) # 2 : nombre d'occurrences
print(scores.index(19)) # 2 : index de la PREMIÈRE occurrence
for score in scores: # le for du niveau 02 parcourt les listes
print(score) # 12 puis 8 puis 19 puis 8
print(sum(scores)) # 47 : sum() additionne toute la liste
print(min(scores), max(scores)) # 8 19
⏩ Le parcours de listes (avec enumerate, la construction en boucle, les pièges) est le cœur de la leçon 03-2.
6. Erreurs fréquentes
1) L'off-by-one de l'index : demander lst[len(lst)]
scores = [12, 8, 15]
print(scores[3]) # IndexError: list index out of range
3 éléments → index 0, 1, 2. len(scores) vaut 3, qui n'existe pas. Le dernier est scores[len(scores) - 1]… c'est-à-dire scores[-1], écris ça. C'est le cousin direct de l'off-by-one de range (⏪ niveau 02).
2) lst = lst.sort() — la liste devient None
nums = [3, 1, 2]
nums = nums.sort() # sort() trie SUR PLACE et renvoie None
print(nums) # None — la liste triée est perdue !
Sur le moment, aucune erreur. Le crash arrive plus loin, au premier nums[0] (section 7). Correction : soit nums.sort() tout seul (sans =), soit nums = sorted(nums). Jamais le mélange des deux.
3) remove d'une valeur absente
fruits = ["pomme", "poire"]
fruits.remove("kiwi") # ValueError: list.remove(x): x not in list
remove exige que la valeur existe. Si tu n'en es pas sûr, protège avec le test de la leçon : if "kiwi" in fruits:.
4) append d'une liste entière (au lieu d'extend)
a = [1, 2]
a.append([3, 4])
print(a, len(a)) # [1, 2, [3, 4]] 3 ← une liste DANS la liste !
b = [1, 2]
b.extend([3, 4]) # extend déverse les éléments un par un
print(b, len(b)) # [1, 2, 3, 4] 4
Aucune erreur, mais a contient 3 éléments dont un est une sous-liste — et tout le code suivant se comporte bizarrement. append ajoute un élément, tel quel.
5) Croire que remove retire toutes les occurrences
letters = ["a", "b", "a"]
letters.remove("a")
print(letters) # ['b', 'a'] — seule la PREMIÈRE occurrence a disparu
Bug silencieux : le programme continue avec un "a" fantôme. Pour tout retirer, il faut une boucle — et boucler en retirant est piégé (⏩ 03-2).
6) Trier une liste aux types mélangés
mixed = [3, "un", 2]
mixed.sort() # TypeError: '<' not supported between instances
# of 'str' and 'int'
Python refuse de décider si "un" est plus petit que 3. Le vrai bug est en amont : cette liste n'aurait jamais dû mélanger textes et nombres (souvent un input() non converti — ⏪ niveau 01).
7. Lire les messages d'erreur
L'exception emblématique de la leçon est IndexError :
Traceback (most recent call last):
File "C:\Users\jtron\Claude\learnpython\best_score.py", line 2, in <module>
print(scores[3])
~~~~~~^^^
IndexError: list index out of range
Lecture — toujours de bas en haut :
- Dernière ligne :
IndexError: list index out of range= « tu demandes un casier qui n'existe pas ». C'est TOUJOURS ça : index trop grand (ou liste vide). - Au-dessus : le fichier, la ligne (
line 2), le code fautif — les^^^pointent l'accès[3], les~~~soulignent la liste concernée. - Réflexe de diagnostic : ajoute
print(len(scores), scores)juste avant la ligne fautive. Tu verras combien de casiers existent vraiment, et pourquoi ton index dépasse.
Deuxième traceback typique — la conséquence différée du bug lst = lst.sort() :
Traceback (most recent call last):
File "C:\Users\jtron\Claude\learnpython\best_score.py", line 3, in <module>
print(nums[0])
~~~~^^^
TypeError: 'NoneType' object is not subscriptable
'NoneType' object is not subscriptable = « tu mets des crochets sur None ». La ligne montrée (nums[0]) n'est pas le bug : elle est la victime. Le bug est plus haut, là où nums est devenu None — cherche un nums = nums.sort() (ou toute méthode « sur place » dont on a gardé le résultat). Quand le type d'une variable te surprend, remonte à sa dernière assignation.
Enfin, ValueError: list.remove(x): x not in list : la valeur demandée n'existe pas dans la liste — affiche la liste juste avant le remove pour voir ce qu'elle contient réellement (faute de frappe ? déjà retirée ?).
8. Exercices — faciles
Règles : un fichier par exercice dans
lessons/level-03-structures-donnees/exercices/mes-reponses/, nomméex-03-1-a.py, etc. Tape tout à la main. Prédis la sortie AVANT d'exécuter.
a) Crée une liste grades avec cinq notes de ton choix (des int). Affiche : la première note, la dernière (avec un index négatif), le nombre de notes, la moyenne (calculée avec sum et len).
b) Prédis la sortie exacte de ce programme sur papier, puis vérifie :
nums = [10, 20, 30]
nums.append(40)
nums.insert(1, 15)
nums.pop()
print(nums, len(nums))
c) Pars de shopping = ["pain", "lait", "oeufs"]. En quatre opérations et un seul print final : ajoute "cafe" au bout, remplace "lait" par "yaourt" (par index), retire "pain" (par valeur), affiche la liste et sa longueur. Sortie attendue : ['yaourt', 'oeufs', 'cafe'] 3.
9. Exercices — moyens
a) Ce programme est censé afficher Meilleur score : 19 mais plante. Trouve les DEUX bugs sans l'exécuter (il y en a un visible et un sournois), puis corrige :
scores = [12, 7, 19, 3]
scores = scores.sort()
print(f"Meilleur score : {scores[len(scores)]}")
b) Avec un for et range (⏪ niveau 02), construis la liste de tous les nombres pairs de 1 à 20 (via append), puis affiche : la liste, sa somme, sa moyenne, et son maximum. Interdiction de taper la liste à la main.
c) Écris un petit contrôleur de saisie : demande un fruit avec input(). Si le fruit est dans stock = ["pomme", "poire", "banane"], retire-le et affiche le stock restant ; sinon affiche Pas en stock : <fruit> sans planter. Teste les deux chemins.
10. Exercices — difficiles
a) Sans utiliser sort() ni sorted() : trouve le deuxième plus grand élément de nums = [12, 7, 19, 3, 15] en un seul parcours. Le programme doit afficher 19 15 (le plus grand, puis le deuxième). Teste ensuite avec d'autres listes d'au moins deux éléments.
- Indice 1 : en parcourant la liste, que dois-tu retenir à chaque instant pour pouvoir répondre à la fin ?
- Indice 2 : deux variables,
bestetsecond. Initialise-les avec les deux premiers éléments — dans le bon ordre (unif). - Indice 3 : pour chaque élément suivant (
for i in range(2, len(nums))) : s'il dépassebest, l'ancienbestdevientsecond; sinon, s'il dépassesecond… termine seul.
11. Mini-projet lié — « liste de courses interactive »
Écris shopping_list.py : un menu en boucle (⏪ while du niveau 02) :
1. Ajouter 2. Retirer 3. Afficher trie q. Quitter
> 1
Article a ajouter : cafe
cafe ajoute (1 article(s)).
Contraintes : la liste démarre vide ; « Retirer » vérifie avec in avant remove (message clair si absent) ; « Afficher » montre les articles triés alphabétiquement, un par ligne, préfixés de -, sans détruire l'ordre d'ajout de la liste (indice : sort ou sorted ?) ; liste vide → message Liste vide. ; choix inconnu → message, on redemande. Durée cible : 30–40 min. C'est le premier mini-projet du niveau annoncé dans le README — tu viens d'écrire ta première vraie application de gestion de données.
12. Correction / méthode de correction
Les solutions détaillées sont dans solutions/serie-03-1-solutions.md.
Méthode : (1) finis TOUS les exercices avant d'ouvrir les solutions ; (2) compare le raisonnement, pas seulement le code ; (3) pour chaque écart, une ligne de journal : « je pensais X, en fait Y » ; (4) refais de tête l'exercice raté le lendemain. Pour les exercices de prédiction : si ta prédiction était fausse, ne te contente pas de lire la bonne réponse — déroule le programme ligne par ligne en dessinant la liste après chaque opération.
13. À retenir
- Une liste = casiers numérotés depuis 0 ; le dernier est
lst[-1]. len(lst)compte ; le dernier index valide estlen(lst) - 1.append(au bout),insert(i, x),remove(valeur),pop(index)—poprenvoie l'élément retiré.lst.sort()modifie sur place et renvoieNone;sorted(lst)renvoie une nouvelle liste. Jamaislst = lst.sort().x in lstteste la présence — à utiliser avantremove.removene retire que la première occurrence.IndexError= casier inexistant ; réflexe :print(len(lst), lst).- Pour chaque méthode, demande-toi : elle modifie, ou elle renvoie ?
14. Questions de révision
- Pourquoi
scores[len(scores)]plante-t-il toujours, quel que soit le contenu de la liste ? - Quelle est la différence entre
scores.sort()etsorted(scores)? Que vautscoresaprèsscores = scores.sort()? removeetpopretirent tous les deux un élément : qu'est-ce qui les distingue (argument attendu, valeur renvoyée) ?- Que fait
append([3, 4])de différent d'extend([3, 4])? - Comment retirer une valeur d'une liste sans risquer de
ValueError? - Que contient
lettersaprèsletters = ["a", "b", "a"]puisletters.remove("a")? - Pourquoi un
TypeError: 'NoneType' object is not subscriptablesurnums[0]indique-t-il presque toujours un bug situé PLUS HAUT dans le fichier ?
15. Checklist de compréhension
- Je peux créer une liste et accéder au premier ET au dernier élément sans compter sur mes doigts.
- Je sais dire, pour
append/insert/remove/pop/sort/sorted, si ça modifie sur place ou si ça renvoie une nouvelle valeur. - Je peux expliquer les deux bugs de l'exercice 9-a à quelqu'un d'autre.
- Je peux provoquer volontairement un
IndexErroret unValueErrorderemove, et les lire de bas en haut. - J'ai fait le mini-projet et « retirer un article absent » ne plante pas.
Si une case reste vide : refais les exercices de la section correspondante demain, AVANT d'ouvrir la leçon suivante.
16. Commit conseillé
git add lessons/level-03-structures-donnees/exercices/mes-reponses/
git commit -m "exercises: complete lesson 03-1 lists"
Puis, si c'est ta fin de session : entrée de journal + git push.