Aller au contenu

Chapitre 04 — Les structures de données : listes, tuples, dictionnaires, ensembles

Durée estimée : 5 à 6 heures (cours, exercices et mini-projet)

Prérequis : Chapitre 03 — Les boucles (for, while, range)

Objectifs — à la fin de ce chapitre, tu sauras…

  • créer, lire, modifier et parcourir une liste ;
  • comprendre la différence entre copier une liste et lui donner un deuxième nom ;
  • utiliser un tuple et « déballer » plusieurs valeurs d'un coup ;
  • ranger des informations par nom grâce aux dictionnaires ;
  • éliminer des doublons et comparer des collections avec les ensembles ;
  • choisir la structure adaptée à chaque problème.

Cours

1. Pourquoi des structures de données ?

Jusqu'ici, chaque variable contenait une seule valeur : un nombre, un texte, un booléen. Imagine que tu veuilles retenir les notes de toute une classe :

note1 = 12
note2 = 15
note3 = 9
note4 = 17
# ... et s'il y a 30 élèves ? 300 ?

C'est vite ingérable : impossible de faire une boucle sur note1, note2, note3… Il nous faut une variable capable de contenir plusieurs valeurs à la fois. C'est le rôle des structures de données (on dit aussi collections).

Python en propose quatre, que tu utiliseras en permanence :

Structure Exemple Analogie
liste (list) [12, 15, 9] une étagère numérotée où l'on peut ajouter, retirer, réordonner
tuple (tuple) (48.85, 2.35) une fiche plastifiée : on la lit, on ne la modifie pas
dictionnaire (dict) {"Alice": 12, "Bob": 15} un répertoire : on cherche par nom, pas par numéro
ensemble (set) {"rouge", "vert"} un sac de billes toutes différentes, sans ordre

2. Les listes : créer et lire

Une liste est une suite ordonnée d'éléments, écrite entre crochets [ ], les éléments étant séparés par des virgules.

notes = [12, 15, 9, 17]
prenoms = ["Alice", "Bob", "Chloé"]
vide = []
melange = [42, "texte", 3.14, True]   # possible, mais rarement une bonne idée

print(notes)
print(prenoms)
print(len(notes))   # len() donne le nombre d'éléments
print(vide)
[12, 15, 9, 17]
['Alice', 'Bob', 'Chloé']
4
[]

Python affiche les chaînes d'une liste avec des apostrophes '…' : c'est normal, c'est sa façon de te montrer qu'il s'agit de textes.

Les indices : chaque case a un numéro

Chaque élément a une position, appelée indice. Attention : on commence à compter à 0 !

 indice :     0     1     2     3
 notes  : [  12,   15,    9,   17 ]
 indice :    -4    -3    -2    -1
notes = [12, 15, 9, 17]
print(notes[0])    # → 12
print(notes[2])    # → 9
print(notes[-1])   # → 17
print(notes[-2])   # → 9

Les indices négatifs comptent depuis la fin : -1 est le dernier élément, -2 l'avant-dernier… Très pratique quand on ne connaît pas la longueur de la liste.

Si tu demandes une case qui n'existe pas, Python se fâche :

notes = [12, 15, 9, 17]
print(notes[4])
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
IndexError: list index out of range

Une liste de 4 éléments a des indices de 0 à 3. Le dernier indice valide est toujours len(liste) - 1.

Modifier un élément

Une liste est modifiable (on dit mutable) : on peut remplacer un élément en l'affectant par son indice.

notes = [12, 15, 9, 17]
notes[2] = 11          # on corrige la 3e note
print(notes)           # → [12, 15, 11, 17]

3. Ajouter et retirer des éléments

Les listes viennent avec des méthodes : des actions qu'on appelle avec un point, liste.methode(...). Voici les plus utiles pour faire grandir ou rétrécir une liste.

Ajouter

courses = ["pain", "lait"]

courses.append("oeufs")          # ajoute À LA FIN
print(courses)                   # → ['pain', 'lait', 'oeufs']

courses.insert(0, "café")        # insère à l'indice 0 (au début)
print(courses)                   # → ['café', 'pain', 'lait', 'oeufs']

courses.extend(["riz", "pâtes"]) # ajoute PLUSIEURS éléments à la fin
print(courses)                   # → ['café', 'pain', 'lait', 'oeufs', 'riz', 'pâtes']

On peut aussi concaténer deux listes avec + (cela crée une nouvelle liste) ou répéter une liste avec * :

a = [1, 2]
b = [3, 4]
print(a + b)     # → [1, 2, 3, 4]
print(a * 3)     # → [1, 2, 1, 2, 1, 2]
print(a)         # → [1, 2]

Retirer

courses = ["café", "pain", "lait", "oeufs", "lait"]

courses.remove("lait")      # retire la PREMIÈRE occurrence de "lait"
print(courses)              # → ['café', 'pain', 'oeufs', 'lait']

dernier = courses.pop()     # retire ET renvoie le dernier élément
print(dernier)              # → lait
print(courses)              # → ['café', 'pain', 'oeufs']

premier = courses.pop(0)    # retire ET renvoie l'élément d'indice 0
print(premier)              # → café

del courses[0]              # supprime l'élément d'indice 0 (sans le renvoyer)
print(courses)              # → ['oeufs']

courses.clear()             # vide complètement la liste
print(courses)              # → []

Résumé :

Méthode Effet
liste.append(x) ajoute x à la fin
liste.insert(i, x) insère x à l'indice i
liste.extend(autre) ajoute tous les éléments de autre à la fin
liste.remove(x) retire la première occurrence de x (erreur si absent)
liste.pop() / liste.pop(i) retire et renvoie le dernier élément / celui d'indice i
del liste[i] supprime l'élément d'indice i
liste.clear() vide la liste

4. Rechercher dans une liste

Le mot-clé in

Pour savoir si un élément est présent, on utilise in (et not in), qui renvoie un booléen. Tu peux donc l'utiliser directement dans un if :

fruits = ["pomme", "banane", "kiwi"]
print("kiwi" in fruits)        # → True
print("mangue" in fruits)      # → False
print("mangue" not in fruits)  # → True

if "banane" in fruits:
    print("Il reste des bananes !")  # → Il reste des bananes !

index et count

lettres = ["a", "b", "c", "b", "a", "b"]
print(lettres.index("c"))    # → 2
print(lettres.count("b"))    # → 3
print(lettres.count("z"))    # → 0

index() provoque une erreur si l'élément est absent (ValueError: 'z' is not in list) : vérifie d'abord avec in.

Les fonctions min, max, sum

Ces fonctions intégrées travaillent sur une liste entière :

notes = [12, 15, 9, 17, 14]
print(min(notes))                  # → 9
print(max(notes))                  # → 17
print(sum(notes))                  # → 67
moyenne = sum(notes) / len(notes)
print(f"Moyenne : {moyenne:.2f}")  # → Moyenne : 13.40

5. Parcourir une liste

Avec for : la façon naturelle

Tu as vu for au chapitre 03 avec range. En réalité, for sait parcourir n'importe quelle collection : à chaque tour, la variable prend la valeur de l'élément suivant.

prenoms = ["Alice", "Bob", "Chloé"]
for prenom in prenoms:
    print(f"Bonjour {prenom} !")
Bonjour Alice !
Bonjour Bob !
Bonjour Chloé !

Lis-le comme une phrase : « pour chaque prénom dans la liste des prénoms, affiche… ». Choisis un nom au singulier pour la variable de boucle et au pluriel pour la liste : le code devient lisible.

Quand on a besoin de l'indice

Parfois, tu veux aussi la position. La façon simple avec ce que tu connais : range(len(liste)).

podium = ["Alice", "Bob", "Chloé"]
for i in range(len(podium)):
    print(f"{i + 1}. {podium[i]}")
1. Alice
2. Bob
3. Chloé

Python propose une fonction plus élégante pour cela, enumerate(). On la verra au chapitre 06.

Construire une liste au fil d'une boucle

Un schéma ultra-courant : partir d'une liste vide et la remplir avec append.

notes = [12, 7, 15, 9, 18, 10]
notes_admises = []
for note in notes:
    if note >= 10:
        notes_admises.append(note)
print(notes_admises)   # → [12, 15, 18, 10]

Et pour un accumulateur (total, compteur), c'est le même principe qu'au chapitre 03 :

temperatures = [18.5, 21.0, 19.5, 23.0]
total = 0
nb_chaudes = 0
for temperature in temperatures:
    total += temperature
    if temperature > 20:
        nb_chaudes += 1
print(f"Moyenne : {total / len(temperatures)}")  # → Moyenne : 20.5
print(f"Jours chauds : {nb_chaudes}")            # → Jours chauds : 2

Remplir une liste avec input()

achats = []
saisie = input("Article (vide pour finir) : ")
while saisie != "":
    achats.append(saisie)
    saisie = input("Article (vide pour finir) : ")
print(f"Tu as noté {len(achats)} article(s) : {achats}")
Article (vide pour finir) : tomates
Article (vide pour finir) : basilic
Article (vide pour finir) : 
Tu as noté 2 article(s) : ['tomates', 'basilic']

6. Trier et inverser

sort() : trier la liste elle-même

nombres = [5, 2, 9, 1, 7]
nombres.sort()
print(nombres)               # → [1, 2, 5, 7, 9]
nombres.sort(reverse=True)   # ordre décroissant
print(nombres)               # → [9, 7, 5, 2, 1]

sort() modifie la liste et ne renvoie rien (elle renvoie None, voir les pièges plus bas).

sorted() : obtenir une copie triée

nombres = [5, 2, 9, 1, 7]
tries = sorted(nombres)
print(tries)     # → [1, 2, 5, 7, 9]
print(nombres)   # → [5, 2, 9, 1, 7]

sorted() laisse l'original intact et renvoie une nouvelle liste. Les chaînes se trient par ordre alphabétique… avec une subtilité : les majuscules passent avant les minuscules.

mots = ["poire", "Banane", "abricot", "Cerise"]
print(sorted(mots))             # → ['Banane', 'Cerise', 'abricot', 'poire']
print(sorted(mots, key=len))    # → ['poire', 'Banane', 'Cerise', 'abricot']

Le paramètre key=len signifie « trie selon la longueur ». On approfondira sorted et key au chapitre 06.

reverse() : inverser l'ordre

etapes = ["départ", "milieu", "arrivée"]
etapes.reverse()
print(etapes)   # → ['arrivée', 'milieu', 'départ']

7. Le découpage (slicing) : extraire une portion

On peut extraire un morceau de liste avec la syntaxe liste[debut:fin]. L'élément d'indice debut est inclus, celui d'indice fin est exclu (comme avec range).

lettres = ["a", "b", "c", "d", "e", "f"]
print(lettres[1:4])    # → ['b', 'c', 'd']
print(lettres[:3])     # → ['a', 'b', 'c']
print(lettres[3:])     # → ['d', 'e', 'f']
print(lettres[-2:])    # → ['e', 'f']
print(lettres[::2])    # → ['a', 'c', 'e']
print(lettres[::-1])   # → ['f', 'e', 'd', 'c', 'b', 'a']
  • Si on omet debut, on part du début ; si on omet fin, on va jusqu'au bout.
  • Un troisième nombre donne le pas : [::2] prend un élément sur deux, [::-1] parcourt à l'envers.

Le slicing fonctionne aussi sur les chaînes de caractères ; on l'étudiera en détail au chapitre 06.


8. Les listes de listes

Une liste peut contenir… d'autres listes. C'est idéal pour représenter un tableau (une grille, une matrice, un plateau de jeu).

grille = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]
print(grille[0])      # → [1, 2, 3]
print(grille[1][2])   # → 6
print(len(grille))    # → 3

grille[1][2] se lit « dans la ligne d'indice 1, l'élément d'indice 2 ». Pour tout parcourir, on utilise deux boucles imbriquées (vues au chapitre 03) :

grille = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]
for ligne in grille:
    for valeur in ligne:
        print(valeur, end=" ")
    print()   # retour à la ligne après chaque ligne de la grille
1 2 3 
4 5 6 
7 8 9 

Exemple concret : un petit tableau d'élèves avec leurs notes.

classe = [
    ["Alice", 14, 16],
    ["Bob", 9, 12],
    ["Chloé", 18, 15],
]
for eleve in classe:
    nom = eleve[0]
    moyenne = (eleve[1] + eleve[2]) / 2
    print(f"{nom} : {moyenne}")
Alice : 15.0
Bob : 10.5
Chloé : 16.5

9. Références et copies : le point le plus important du chapitre

Voici un comportement qui surprend tous les débutants :

a = [1, 2, 3]
b = a
b.append(4)
print(a)   # → [1, 2, 3, 4]
print(b)   # → [1, 2, 3, 4]

On a modifié b… et a a changé aussi ! Pourquoi ?

Analogie : une variable n'est pas une boîte qui contient la liste, c'est une étiquette collée sur la liste. L'instruction b = a ne fabrique pas une deuxième liste : elle colle une deuxième étiquette, b, sur la même liste.

  a ──┐
      ├──►  [1, 2, 3, 4]
  b ──┘

On peut le vérifier avec l'opérateur is, qui teste si deux noms désignent le même objet :

a = [1, 2, 3]
b = a
print(b is a)   # → True

Faire une vraie copie

Si tu veux une liste indépendante, il faut la copier explicitement. Trois façons équivalentes :

a = [1, 2, 3]
b = a.copy()    # méthode copy()
c = list(a)     # construire une nouvelle liste à partir de a
d = a[:]        # slicing complet
b.append(4)
print(a)        # → [1, 2, 3]
print(b)        # → [1, 2, 3, 4]
print(c is a)   # → False
print(c == a)   # → True

Note la différence : == compare le contenu, is compare l'identité (même objet ou non).

Et pour les nombres ou les chaînes ?

Avec un int ou une str, ce problème ne se pose jamais : ces types sont immuables (on ne peut pas les modifier sur place). x += 1 ne modifie pas le nombre, il crée un nouveau nombre et déplace l'étiquette.

x = 5
y = x
x += 1
print(x, y)   # → 6 5

Copie superficielle et listes imbriquées

copy() ne copie que le premier niveau. Pour une liste de listes, les sous-listes restent partagées :

grille = [[1, 2], [3, 4]]
copie = grille.copy()
copie[0].append(99)
print(grille)   # → [[1, 2, 99], [3, 4]]

Pour copier aussi les sous-listes, on utilise deepcopy du module copy (les modules seront vus au chapitre 09) :

import copy

grille = [[1, 2], [3, 4]]
copie = copy.deepcopy(grille)
copie[0].append(99)
print(grille)   # → [[1, 2], [3, 4]]
print(copie)    # → [[1, 2, 99], [3, 4]]

10. Les tuples : des listes figées

Un tuple ressemble à une liste, mais il est immuable : une fois créé, on ne peut ni modifier, ni ajouter, ni retirer d'élément. On l'écrit entre parenthèses.

point = (3, 7)
couleur_rgb = (255, 128, 0)
print(point[0])         # → 3
print(len(couleur_rgb)) # → 3
print(128 in couleur_rgb)  # → True

Tenter de le modifier provoque une erreur :

point = (3, 7)
point[0] = 10
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: 'tuple' object does not support item assignment

À quoi bon ? Un tuple sert à regrouper des valeurs qui vont ensemble et ne doivent pas changer : des coordonnées, une date (14, 7, 1789), une couleur RGB… L'immuabilité est une garantie : personne ne pourra modifier ces données par erreur.

Le tuple à un seul élément

Piège classique : (5) n'est pas un tuple, c'est juste le nombre 5 entre parenthèses. Il faut une virgule :

pas_un_tuple = (5)
un_tuple = (5,)
print(type(pas_un_tuple))  # → <class 'int'>
print(type(un_tuple))      # → <class 'tuple'>

En fait, c'est la virgule qui fait le tuple, pas les parenthèses : point = 3, 7 crée aussi un tuple.

Le déballage (unpacking)

On peut répartir les éléments d'un tuple (ou d'une liste) dans plusieurs variables d'un coup :

point = (3, 7)
x, y = point
print(x)   # → 3
print(y)   # → 7

jour, mois, annee = (14, 7, 1789)
print(f"{jour}/{mois}/{annee}")   # → 14/7/1789

Il faut autant de variables que d'éléments, sinon : ValueError: too many values to unpack.

L'astérisque * permet de récupérer « le reste » dans une liste :

premier, *autres = [10, 20, 30, 40]
print(premier)   # → 10
print(autres)    # → [20, 30, 40]

Le déballage offre une astuce célèbre : échanger deux variables sans variable temporaire.

a = 1
b = 2
a, b = b, a
print(a, b)   # → 2 1

Et il est très pratique dans une boucle sur une liste de tuples :

villes = [("Paris", 2_100_000), ("Lyon", 520_000), ("Lille", 236_000)]
for nom, habitants in villes:
    print(f"{nom} compte {habitants} habitants")
Paris compte 2100000 habitants
Lyon compte 520000 habitants
Lille compte 236000 habitants

Rappel : 2_100_000 est simplement une écriture plus lisible de 2100000 ; les _ sont ignorés par Python.


11. Les dictionnaires : chercher par clé

Une liste range ses éléments par numéro. Mais souvent, on veut retrouver une information par un nom : le numéro de téléphone de Bob, le prix d'une baguette, la capitale de l'Italie… C'est le rôle du dictionnaire.

Analogie : un vrai dictionnaire associe un mot (la clé) à sa définition (la valeur). On ne cherche pas « la définition n° 4127 », on cherche « le mot chat ».

prix = {"pain": 1.20, "lait": 0.95, "oeufs": 2.50}
print(prix["lait"])   # → 0.95
print(len(prix))      # → 3
print(prix)           # → {'pain': 1.2, 'lait': 0.95, 'oeufs': 2.5}
  • On écrit un dictionnaire entre accolades { }, avec des paires cle: valeur séparées par des virgules.
  • Les clés sont uniques et doivent être immuables (chaînes, nombres, tuples). En pratique, ce sont presque toujours des chaînes.
  • Les valeurs peuvent être de n'importe quel type, y compris des listes ou d'autres dictionnaires.
  • Depuis Python 3.7, un dictionnaire conserve l'ordre d'insertion.

Ajouter, modifier, supprimer

stock = {"pommes": 10, "poires": 4}

stock["kiwis"] = 7         # la clé n'existe pas : on l'AJOUTE
stock["pommes"] = 12       # la clé existe : on la MODIFIE
stock["poires"] += 3       # on peut utiliser += sur la valeur
print(stock)               # → {'pommes': 12, 'poires': 7, 'kiwis': 7}

del stock["kiwis"]         # suppression
print(stock)               # → {'pommes': 12, 'poires': 7}

quantite = stock.pop("poires")   # supprime ET renvoie la valeur
print(quantite)            # → 7
print(stock)               # → {'pommes': 12}

Lire sans risque : in et get

Accéder à une clé absente avec [ ] provoque une erreur KeyError :

stock = {"pommes": 10}
print(stock["bananes"])
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
KeyError: 'bananes'

Deux solutions :

stock = {"pommes": 10}

# 1. Vérifier avec in (in teste les CLÉS)
if "bananes" in stock:
    print(stock["bananes"])
else:
    print("Pas de bananes")          # → Pas de bananes

# 2. Utiliser get(cle, valeur_par_defaut)
print(stock.get("bananes", 0))       # → 0
print(stock.get("pommes", 0))        # → 10
print(stock.get("bananes"))          # → None

Sans valeur par défaut, get renvoie None, la valeur spéciale qui signifie « rien ».

Parcourir un dictionnaire

capitales = {"France": "Paris", "Italie": "Rome", "Espagne": "Madrid"}

for pays in capitales:            # par défaut : on parcourt les CLÉS
    print(pays)

for ville in capitales.values():  # les valeurs
    print(ville)

for pays, ville in capitales.items():   # les paires (clé, valeur)
    print(f"La capitale de {pays} est {ville}.")
France
Italie
Espagne
Paris
Rome
Madrid
La capitale de France est Paris.
La capitale de Italie est Rome.
La capitale de Espagne est Madrid.

items() renvoie des tuples (cle, valeur) que l'on déballe directement dans la boucle : c'est la forme que tu utiliseras le plus.

Les méthodes keys(), values() et items() renvoient des « vues » ; si tu as besoin d'une vraie liste, convertis avec list() :

capitales = {"France": "Paris", "Italie": "Rome"}
print(list(capitales.keys()))    # → ['France', 'Italie']
print(list(capitales.values()))  # → ['Paris', 'Rome']

Un grand classique : compter avec un dictionnaire

Compter combien de fois apparaît chaque élément est un usage incontournable des dictionnaires.

votes = ["chat", "chien", "chat", "lapin", "chat", "chien"]
compteur = {}
for animal in votes:
    if animal in compteur:
        compteur[animal] += 1
    else:
        compteur[animal] = 1
print(compteur)   # → {'chat': 3, 'chien': 2, 'lapin': 1}

Avec get, c'est encore plus court :

votes = ["chat", "chien", "chat", "lapin", "chat", "chien"]
compteur = {}
for animal in votes:
    compteur[animal] = compteur.get(animal, 0) + 1
print(compteur)   # → {'chat': 3, 'chien': 2, 'lapin': 1}

Le module collections propose un outil tout prêt pour ça, Counter. Tu le découvriras au chapitre 09.

Dictionnaires imbriqués et listes de dictionnaires

Les structures se combinent. Une liste de dictionnaires est la façon standard de représenter un tableau de données (chaque dictionnaire est une ligne) :

contacts = [
    {"nom": "Alice", "tel": "06 11 22 33 44", "ville": "Lyon"},
    {"nom": "Bob", "tel": "07 55 66 77 88", "ville": "Nantes"},
]
for contact in contacts:
    print(f"{contact['nom']} ({contact['ville']}) : {contact['tel']}")
Alice (Lyon) : 06 11 22 33 44
Bob (Nantes) : 07 55 66 77 88

Dans une f-string délimitée par des guillemets "…", utilise des apostrophes '…' pour les clés, afin de ne pas fermer la chaîne par erreur.

Et un dictionnaire de listes permet de regrouper plusieurs valeurs par clé :

notes_par_eleve = {
    "Alice": [14, 16, 12],
    "Bob": [9, 12],
}
notes_par_eleve["Bob"].append(15)
notes_par_eleve["Chloé"] = [18]
for eleve, notes in notes_par_eleve.items():
    print(f"{eleve} : {notes}, moyenne {sum(notes) / len(notes):.1f}")
Alice : [14, 16, 12], moyenne 14.0
Bob : [9, 12, 15], moyenne 12.0
Chloé : [18], moyenne 18.0

12. Les ensembles (set) : des éléments uniques

Un ensemble est une collection sans ordre et sans doublon. On l'écrit entre accolades, mais sans paires cle: valeur.

couleurs = {"rouge", "vert", "bleu", "rouge"}
print(len(couleurs))         # → 3
print("vert" in couleurs)    # → True

Le doublon "rouge" a disparu automatiquement. Comme il n'y a pas d'ordre, pas d'indice : couleurs[0] provoque une erreur TypeError. L'ordre d'affichage d'un ensemble de chaînes peut même changer d'une exécution à l'autre ; pour un affichage stable, utilise sorted(ensemble), qui renvoie une liste triée.

Créer, ajouter, retirer

vide = set()          # ATTENTION : {} crée un dictionnaire vide, pas un ensemble !
vide.add(3)
vide.add(1)
vide.add(3)           # déjà présent : ne fait rien
print(sorted(vide))   # → [1, 3]

vide.discard(3)       # retire 3 (aucune erreur s'il est absent)
vide.discard(42)      # pas d'erreur
print(vide)           # → {1}

remove(x) existe aussi, mais provoque une KeyError si x est absent ; discard(x) est plus tolérant.

Supprimer les doublons d'une liste

C'est l'usage le plus courant :

emails = ["a@x.fr", "b@x.fr", "a@x.fr", "c@x.fr", "b@x.fr"]
uniques = set(emails)
print(len(uniques))       # → 3
print(sorted(uniques))    # → ['a@x.fr', 'b@x.fr', 'c@x.fr']

Les opérations ensemblistes

Comme en mathématiques, on peut combiner des ensembles :

club_foot = {"Alice", "Bob", "Chloé", "David"}
club_theatre = {"Chloé", "Emma", "Bob", "Farid"}

print(sorted(club_foot | club_theatre))  # → ['Alice', 'Bob', 'Chloé', 'David', 'Emma', 'Farid']
print(sorted(club_foot & club_theatre))  # → ['Bob', 'Chloé']
print(sorted(club_foot - club_theatre))  # → ['Alice', 'David']
print(sorted(club_foot ^ club_theatre))  # → ['Alice', 'David', 'Emma', 'Farid']
Opérateur Nom Résultat
a \| b union les éléments présents dans a ou b
a & b intersection les éléments présents dans a et b
a - b différence les éléments de a qui ne sont pas dans b
a ^ b différence symétrique les éléments présents dans un seul des deux

Bonus : tester x in ensemble est extrêmement rapide, même avec des millions d'éléments, alors que x in liste doit parcourir toute la liste. Quand tu dois faire beaucoup de tests d'appartenance, pense aux ensembles.


13. Choisir la bonne structure

Besoin Structure
Une suite d'éléments dans un ordre précis, qui va évoluer liste
Quelques valeurs liées qui ne doivent pas changer (coordonnées, date) tuple
Retrouver une valeur à partir d'un nom/identifiant dictionnaire
Des éléments uniques, tester l'appartenance, comparer deux groupes ensemble

Et le tableau des propriétés :

Ordonné Modifiable Doublons Accès
list oui oui autorisés par indice l[0]
tuple oui non autorisés par indice t[0]
dict oui (ordre d'insertion) oui clés uniques par clé d["cle"]
set non oui interdits aucun (seulement in)

Toutes ces structures fonctionnent avec len(), in et for. Et les conversions sont faciles : list(...), tuple(...), set(...), dict(...).

lettres = list("bonjour")
print(lettres)             # → ['b', 'o', 'n', 'j', 'o', 'u', 'r']
print(tuple([1, 2, 3]))    # → (1, 2, 3)
print(sorted(set("bonjour")))   # → ['b', 'j', 'n', 'o', 'r', 'u']
print(dict([("a", 1), ("b", 2)]))   # → {'a': 1, 'b': 2}

Pièges fréquents

Piège 1 — Croire que b = a copie une liste

originale = ["pain", "lait"]
sauvegarde = originale          # pas une copie : un deuxième nom !
originale.append("beurre")
print(sauvegarde)               # → ['pain', 'lait', 'beurre']

Aucun message d'erreur : c'est ce qui rend ce bug sournois. Correction :

originale = ["pain", "lait"]
sauvegarde = originale.copy()   # vraie copie
originale.append("beurre")
print(sauvegarde)               # → ['pain', 'lait']

Piège 2 — Modifier une liste pendant qu'on la parcourt

On veut retirer toutes les notes inférieures à 10 :

notes = [8, 5, 12, 9, 15]
for note in notes:
    if note < 10:
        notes.remove(note)
print(notes)   # → [5, 12, 15]

Le 5 est resté ! En retirant le 8, tous les éléments ont « glissé » d'une case vers la gauche, et la boucle a sauté le 5. Correction : construire une nouvelle liste (ou parcourir une copie).

notes = [8, 5, 12, 9, 15]
notes_gardees = []
for note in notes:
    if note >= 10:
        notes_gardees.append(note)
notes = notes_gardees
print(notes)   # → [12, 15]

Pour un dictionnaire, c'est encore plus strict : Python refuse carrément.

stock = {"pommes": 0, "poires": 4}
for fruit in stock:
    if stock[fruit] == 0:
        del stock[fruit]
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: dictionary changed size during iteration

Correction : parcourir une copie des clés, for fruit in list(stock):.

stock = {"pommes": 0, "poires": 4}
for fruit in list(stock):
    if stock[fruit] == 0:
        del stock[fruit]
print(stock)   # → {'poires': 4}

Piège 3 — liste = liste.sort()

nombres = [3, 1, 2]
nombres = nombres.sort()
print(nombres)   # → None

sort() trie la liste sur place et renvoie None. En réaffectant, tu as perdu ta liste. Correction : nombres.sort() tout seul, ou nombres = sorted(nombres).

Piège 4 — Sortir des indices de la liste

nombres = [10, 20, 30]
for i in range(len(nombres) + 1):
    print(nombres[i])
10
20
30
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
IndexError: list index out of range

Le dernier indice valide est len(nombres) - 1. Correction : range(len(nombres)), ou mieux, for nombre in nombres:.

Piège 5 — Accéder à une clé absente d'un dictionnaire

age = {"Alice": 30}
print(age["alice"])   # attention à la casse !
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
KeyError: 'alice'

Correction : vérifier avec if "alice" in age: ou utiliser age.get("alice", "inconnu"). Et rappelle-toi que "Alice" et "alice" sont deux clés différentes.

Piège 6 — {} n'est pas un ensemble vide

deja_vus = {}
deja_vus.add("Paris")
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: 'dict' object has no attribute 'add'

Correction : deja_vus = set().

Piège 7 — append au lieu de extend

liste = [1, 2]
liste.append([3, 4])
print(liste)   # → [1, 2, [3, 4]]

append ajoute un élément (ici, une liste entière). Correction : liste.extend([3, 4]) donne [1, 2, 3, 4].

Piège 8 — Créer une grille avec *

grille = [[0] * 3] * 3
grille[0][0] = 1
print(grille)   # → [[1, 0, 0], [1, 0, 0], [1, 0, 0]]

Les trois lignes sont la même liste (trois étiquettes sur un seul objet, cf. section 9). Correction : créer chaque ligne séparément dans une boucle.

grille = []
for _ in range(3):
    grille.append([0] * 3)
grille[0][0] = 1
print(grille)   # → [[1, 0, 0], [0, 0, 0], [0, 0, 0]]

Le nom de variable _ est une convention qui signifie « je n'utilise pas cette variable ».


Exercices

Exercice 1 — Statistiques de notes ⭐

On donne notes = [12, 8, 15, 19, 7, 11, 14]. Affiche le nombre de notes, la plus petite, la plus grande, la moyenne arrondie à 2 décimales, et le nombre de notes supérieures ou égales à 10.

Sortie attendue :

Nombre de notes : 7
Minimum : 7
Maximum : 19
Moyenne : 12.29
Notes >= 10 : 5

Exercice 2 — Manipulations de liste ⭐

Pars de animaux = ["chat", "chien", "lapin"] et, dans l'ordre : 1. ajoute "hamster" à la fin ; 2. insère "poisson" en première position ; 3. retire "chien" ; 4. affiche la liste, son premier et son dernier élément ; 5. affiche la liste triée par ordre alphabétique, puis la liste à l'envers (sans la trier).

Sortie attendue :

['poisson', 'chat', 'lapin', 'hamster']
Premier : poisson
Dernier : hamster
Triée : ['chat', 'hamster', 'lapin', 'poisson']
À l'envers : ['hamster', 'lapin', 'chat', 'poisson']

Exercice 3 — Compter les mots ⭐⭐

On donne la phrase phrase = "le chat mange le poisson et le chien mange le chat". La méthode phrase.split() découpe une chaîne en liste de mots (on la reverra au chapitre 06). Construis un dictionnaire qui associe chaque mot à son nombre d'apparitions, puis affiche chaque mot avec son compte, et enfin le mot le plus fréquent.

Sortie attendue :

le : 4
chat : 2
mange : 2
poisson : 1
et : 1
chien : 1
Mot le plus fréquent : 'le' (4 fois)

Exercice 4 — Petit annuaire ⭐⭐

On donne :

annuaire = {"Alice": "06 12 34 56 78", "Bob": "07 98 76 54 32", "Chloé": "06 55 44 33 22"}

Demande à l'utilisateur des prénoms en boucle (vide pour arrêter). Pour chaque prénom, affiche le numéro s'il existe, sinon propose de l'ajouter en demandant le numéro. À la fin, affiche le nombre de contacts.

Exemple d'exécution :

Prénom (vide pour quitter) : Bob
Bob : 07 98 76 54 32
Prénom (vide pour quitter) : David
David est inconnu. Son numéro : 06 00 00 00 00
David ajouté.
Prénom (vide pour quitter) : 
L'annuaire contient 4 contacts.

Exercice 5 — Amis en commun ⭐⭐

On donne les listes d'amis de deux personnes (avec des doublons dus à une erreur de saisie) :

amis_lea = ["Tom", "Inès", "Hugo", "Tom", "Sarah", "Yanis"]
amis_noe = ["Sarah", "Lina", "Tom", "Hugo", "Lina"]

Affiche (toujours triés par ordre alphabétique) : les amis communs, les amis de Léa que Noé ne connaît pas, et tous les amis sans doublon, avec leur nombre.

Sortie attendue :

Amis communs : ['Hugo', 'Sarah', 'Tom']
Seulement chez Léa : ['Inès', 'Yanis']
Tous (6) : ['Hugo', 'Inès', 'Lina', 'Sarah', 'Tom', 'Yanis']

Exercice 6 — Transposer une grille ⭐⭐

On donne une grille de 2 lignes et 3 colonnes :

grille = [
    [1, 2, 3],
    [4, 5, 6],
]

Construis sa transposée (les lignes deviennent les colonnes), de 3 lignes et 2 colonnes, avec des boucles, puis affiche-la ligne par ligne.

Sortie attendue :

[1, 4]
[2, 5]
[3, 6]

Exercice 7 — Gestion de stock ⭐⭐⭐

Un magasin a ce stock et reçoit cette liste de commandes (tuples (article, quantité)) :

stock = {"stylo": 50, "cahier": 20, "gomme": 5, "règle": 0}
commandes = [("stylo", 10), ("gomme", 8), ("cahier", 20), ("règle", 1), ("trousse", 2)]

Traite les commandes dans l'ordre : - si l'article n'existe pas, affiche trousse : article inconnu ; - si le stock est insuffisant, affiche gomme : stock insuffisant (5 disponible(s)) et ne retire rien ; - sinon, retire la quantité et affiche stylo : 10 expédié(s).

Enfin, affiche le stock final, puis la liste triée des articles en rupture (quantité 0).

Sortie attendue :

stylo : 10 expédié(s)
gomme : stock insuffisant (5 disponible(s))
cahier : 20 expédié(s)
règle : stock insuffisant (0 disponible(s))
trousse : article inconnu
Stock final : {'stylo': 40, 'cahier': 0, 'gomme': 5, 'règle': 0}
En rupture : ['cahier', 'règle']

Exercice 8 — Classement des joueurs ⭐⭐⭐

On donne les scores d'une partie :

scores = {"Léo": 1200, "Maya": 1850, "Sam": 900, "Inès": 1850, "Noah": 1500}

Affiche le classement du meilleur au moins bon score. En cas d'égalité, classe par ordre alphabétique et donne le même rang aux ex æquo.

Indice : construis une liste de tuples (score, nom) et trie-la. Python compare les tuples élément par élément : d'abord le score, puis le nom en cas d'égalité. Comme on veut les scores décroissants mais les noms croissants, une astuce consiste à stocker -score.

Sortie attendue :

1. Inès - 1850 pts
1. Maya - 1850 pts
3. Noah - 1500 pts
4. Léo - 1200 pts
5. Sam - 900 pts

Mini-projet — Le gestionnaire de liste de courses

Cahier des charges

Écris un programme interactif qui gère une liste de courses. Les articles sont stockés dans un dictionnaire {article: quantite}. Le programme affiche un menu en boucle :

=== LISTE DE COURSES ===
1. Ajouter un article
2. Retirer un article
3. Afficher la liste
4. Vider la liste
5. Quitter
  • Ajouter : demander le nom et la quantité. Si l'article existe déjà, additionner les quantités. Les noms sont enregistrés en minuscules (saisie.lower()) et sans espaces autour (saisie.strip()) pour que « Pain » et « pain » soient le même article.
  • Retirer : demander le nom ; s'il existe, le supprimer, sinon afficher un message.
  • Afficher : lister les articles par ordre alphabétique, avec leur quantité, et le nombre total d'articles. Si la liste est vide, le dire.
  • Vider : tout supprimer.
  • Quitter : afficher un message d'au revoir et arrêter.
  • Un choix invalide affiche un message d'erreur sans planter.

Exemple d'exécution :

Ton choix : 1
Article : Pain
Quantité : 2
2 x pain ajouté(s).
Ton choix : 1
Article : pain
Quantité : 1
1 x pain ajouté(s).
Ton choix : 3
- pain : 3
Total : 3 article(s)

Étapes suggérées

  1. Crée le dictionnaire vide et une boucle while True qui affiche le menu et lit le choix.
  2. Gère le choix "5" avec break, puis un else pour les choix invalides.
  3. Implémente « Afficher » (avec sorted(courses) pour l'ordre alphabétique).
  4. Implémente « Ajouter » avec courses.get(article, 0) + quantite.
  5. Implémente « Retirer » et « Vider ».
  6. Vérifie que la quantité saisie est bien un nombre avec saisie.isdigit() (renvoie True si la chaîne ne contient que des chiffres) avant d'appeler int().

Pistes d'amélioration

  • Permettre de retirer seulement une partie de la quantité (et supprimer l'article si on tombe à 0).
  • Ajouter un prix unitaire : dictionnaire de dictionnaires {"pain": {"quantite": 2, "prix": 1.20}} et afficher le total à payer.
  • Classer les articles par rayon ({"boulangerie": {...}, "frais": {...}}).
  • Utiliser match/case (chapitre 02) pour le menu.
  • Plus tard : découper le programme en fonctions (chapitre 05) et sauvegarder la liste dans un fichier JSON (chapitre 07).

Corrigés

Corrigé de l'exercice 1
notes = [12, 8, 15, 19, 7, 11, 14]

nb_admis = 0
for note in notes:
    if note >= 10:
        nb_admis += 1

moyenne = sum(notes) / len(notes)

print(f"Nombre de notes : {len(notes)}")
print(f"Minimum : {min(notes)}")
print(f"Maximum : {max(notes)}")
print(f"Moyenne : {moyenne:.2f}")
print(f"Notes >= 10 : {nb_admis}")
Sortie :
Nombre de notes : 7
Minimum : 7
Maximum : 19
Moyenne : 12.29
Notes >= 10 : 5
`min`, `max`, `sum` et `len` font l'essentiel du travail. Le comptage des notes admises utilise le schéma « compteur » : une variable à 0 incrémentée dans la boucle. La f-string `{moyenne:.2f}` arrondit l'affichage à 2 décimales (86 / 7 = 12.2857…).
Corrigé de l'exercice 2
animaux = ["chat", "chien", "lapin"]

animaux.append("hamster")
animaux.insert(0, "poisson")
animaux.remove("chien")

print(animaux)
print(f"Premier : {animaux[0]}")
print(f"Dernier : {animaux[-1]}")
print(f"Triée : {sorted(animaux)}")
print(f"À l'envers : {animaux[::-1]}")
Sortie :
['poisson', 'chat', 'lapin', 'hamster']
Premier : poisson
Dernier : hamster
Triée : ['chat', 'hamster', 'lapin', 'poisson']
À l'envers : ['hamster', 'lapin', 'chat', 'poisson']
On utilise `sorted()` et `[::-1]` plutôt que `sort()` et `reverse()` : ainsi, la liste `animaux` n'est pas modifiée par les affichages. L'indice `-1` donne le dernier élément quelle que soit la longueur.
Corrigé de l'exercice 3
phrase = "le chat mange le poisson et le chien mange le chat"

compteur = {}
for mot in phrase.split():
    compteur[mot] = compteur.get(mot, 0) + 1

for mot, nombre in compteur.items():
    print(f"{mot} : {nombre}")

# Recherche du maximum « à la main »
mot_max = ""
nombre_max = 0
for mot, nombre in compteur.items():
    if nombre > nombre_max:
        mot_max = mot
        nombre_max = nombre

print(f"Mot le plus fréquent : '{mot_max}' ({nombre_max} fois)")
Sortie :
le : 4
chat : 2
mange : 2
poisson : 1
et : 1
chien : 1
Mot le plus fréquent : 'le' (4 fois)
`compteur.get(mot, 0)` renvoie le compte actuel, ou 0 si le mot n'a jamais été vu. Le dictionnaire conserve l'ordre d'insertion, d'où l'ordre d'affichage. Pour le maximum, on garde en mémoire le meilleur trouvé jusqu'ici et on le remplace dès qu'on trouve mieux.
Corrigé de l'exercice 4
annuaire = {"Alice": "06 12 34 56 78", "Bob": "07 98 76 54 32", "Chloé": "06 55 44 33 22"}

prenom = input("Prénom (vide pour quitter) : ")
while prenom != "":
    if prenom in annuaire:
        print(f"{prenom} : {annuaire[prenom]}")
    else:
        numero = input(f"{prenom} est inconnu. Son numéro : ")
        annuaire[prenom] = numero
        print(f"{prenom} ajouté.")
    prenom = input("Prénom (vide pour quitter) : ")

print(f"L'annuaire contient {len(annuaire)} contacts.")
Avec les saisies `Bob`, `David`, `06 00 00 00 00` puis une ligne vide :
Prénom (vide pour quitter) : Bob
Bob : 07 98 76 54 32
Prénom (vide pour quitter) : David
David est inconnu. Son numéro : 06 00 00 00 00
David ajouté.
Prénom (vide pour quitter) : 
L'annuaire contient 4 contacts.
On teste d'abord `prenom in annuaire` pour éviter une `KeyError`. Ajouter un contact, c'est simplement affecter une nouvelle clé : `annuaire[prenom] = numero`.
Corrigé de l'exercice 5
amis_lea = ["Tom", "Inès", "Hugo", "Tom", "Sarah", "Yanis"]
amis_noe = ["Sarah", "Lina", "Tom", "Hugo", "Lina"]

ens_lea = set(amis_lea)
ens_noe = set(amis_noe)

communs = ens_lea & ens_noe
seulement_lea = ens_lea - ens_noe
tous = ens_lea | ens_noe

print(f"Amis communs : {sorted(communs)}")
print(f"Seulement chez Léa : {sorted(seulement_lea)}")
print(f"Tous ({len(tous)}) : {sorted(tous)}")
Sortie :
Amis communs : ['Hugo', 'Sarah', 'Tom']
Seulement chez Léa : ['Inès', 'Yanis']
Tous (6) : ['Hugo', 'Inès', 'Lina', 'Sarah', 'Tom', 'Yanis']
Convertir en `set` élimine les doublons d'un coup, puis les opérateurs `&`, `-` et `|` répondent directement aux questions. `sorted()` transforme l'ensemble en liste triée pour un affichage stable.
Corrigé de l'exercice 6
grille = [
    [1, 2, 3],
    [4, 5, 6],
]

nb_lignes = len(grille)
nb_colonnes = len(grille[0])

transposee = []
for j in range(nb_colonnes):        # chaque colonne de la grille...
    nouvelle_ligne = []
    for i in range(nb_lignes):      # ...devient une ligne de la transposée
        nouvelle_ligne.append(grille[i][j])
    transposee.append(nouvelle_ligne)

for ligne in transposee:
    print(ligne)
Sortie :
[1, 4]
[2, 5]
[3, 6]
L'élément en ligne `i`, colonne `j` de la grille se retrouve en ligne `j`, colonne `i` de la transposée. La boucle extérieure parcourt donc les colonnes, et on crée une **nouvelle** liste à chaque tour (pas de piège 8 ici).
Corrigé de l'exercice 7
stock = {"stylo": 50, "cahier": 20, "gomme": 5, "règle": 0}
commandes = [("stylo", 10), ("gomme", 8), ("cahier", 20), ("règle", 1), ("trousse", 2)]

for article, quantite in commandes:
    if article not in stock:
        print(f"{article} : article inconnu")
    elif stock[article] < quantite:
        print(f"{article} : stock insuffisant ({stock[article]} disponible(s))")
    else:
        stock[article] -= quantite
        print(f"{article} : {quantite} expédié(s)")

print(f"Stock final : {stock}")

ruptures = []
for article, quantite in stock.items():
    if quantite == 0:
        ruptures.append(article)
ruptures.sort()
print(f"En rupture : {ruptures}")
Sortie :
stylo : 10 expédié(s)
gomme : stock insuffisant (5 disponible(s))
cahier : 20 expédié(s)
règle : stock insuffisant (0 disponible(s))
trousse : article inconnu
Stock final : {'stylo': 40, 'cahier': 0, 'gomme': 5, 'règle': 0}
En rupture : ['cahier', 'règle']
Le déballage `for article, quantite in commandes` rend la boucle très lisible. L'ordre des tests compte : on vérifie d'abord que l'article existe (sinon `stock[article]` provoquerait une `KeyError`), puis que la quantité suffit.
Corrigé de l'exercice 8
scores = {"Léo": 1200, "Maya": 1850, "Sam": 900, "Inès": 1850, "Noah": 1500}

# 1. Liste de tuples (-score, nom) : le tri croissant donnera
#    les scores décroissants, puis les noms par ordre alphabétique.
classement = []
for nom, score in scores.items():
    classement.append((-score, nom))
classement.sort()

# 2. Affichage avec gestion des ex æquo
rang = 0
score_precedent = None
for position in range(len(classement)):
    score_negatif, nom = classement[position]
    score = -score_negatif
    if score != score_precedent:
        rang = position + 1     # nouveau score : le rang = la position réelle
    print(f"{rang}. {nom} - {score} pts")
    score_precedent = score
Sortie :
1. Inès - 1850 pts
1. Maya - 1850 pts
3. Noah - 1500 pts
4. Léo - 1200 pts
5. Sam - 900 pts
Les tuples se comparent comme des mots dans le dictionnaire : premier élément d'abord, puis le suivant en cas d'égalité. En stockant `-1850`, `-1500`, etc., le plus grand score devient le plus petit nombre et arrive en tête du tri croissant. Pour les ex æquo, le rang ne change que lorsque le score change, et il saute alors à la position réelle (d'où le « 3 » après deux « 1 », comme dans un vrai classement sportif). Au chapitre 06, tu verras une écriture plus directe avec `sorted(..., key=lambda ...)`.
Corrigé du mini-projet
courses = {}

while True:
    print()
    print("=== LISTE DE COURSES ===")
    print("1. Ajouter un article")
    print("2. Retirer un article")
    print("3. Afficher la liste")
    print("4. Vider la liste")
    print("5. Quitter")
    choix = input("Ton choix : ").strip()

    if choix == "1":
        article = input("Article : ").strip().lower()
        saisie = input("Quantité : ").strip()
        if article == "":
            print("Nom d'article vide, rien n'a été ajouté.")
        elif not saisie.isdigit() or int(saisie) == 0:
            print("La quantité doit être un nombre entier positif.")
        else:
            quantite = int(saisie)
            courses[article] = courses.get(article, 0) + quantite
            print(f"{quantite} x {article} ajouté(s).")

    elif choix == "2":
        article = input("Article à retirer : ").strip().lower()
        if article in courses:
            del courses[article]
            print(f"{article} retiré de la liste.")
        else:
            print(f"{article} n'est pas dans la liste.")

    elif choix == "3":
        if len(courses) == 0:
            print("La liste est vide.")
        else:
            total = 0
            for article in sorted(courses):
                print(f"- {article} : {courses[article]}")
                total += courses[article]
            print(f"Total : {total} article(s)")

    elif choix == "4":
        courses.clear()
        print("Liste vidée.")

    elif choix == "5":
        print("Bonnes courses, à bientôt !")
        break

    else:
        print("Choix invalide, tape un nombre entre 1 et 5.")
Exemple de session (saisies : `1`, `Pain`, `2`, `1`, `pain`, `1`, `1`, `Tomates`, `6`, `3`, `2`, `beurre`, `7`, `5`) :
=== LISTE DE COURSES ===
1. Ajouter un article
2. Retirer un article
3. Afficher la liste
4. Vider la liste
5. Quitter
Ton choix : 1
Article : Pain
Quantité : 2
2 x pain ajouté(s).

=== LISTE DE COURSES ===
1. Ajouter un article
2. Retirer un article
3. Afficher la liste
4. Vider la liste
5. Quitter
Ton choix : 1
Article : pain
Quantité : 1
1 x pain ajouté(s).

=== LISTE DE COURSES ===
1. Ajouter un article
2. Retirer un article
3. Afficher la liste
4. Vider la liste
5. Quitter
Ton choix : 1
Article : Tomates
Quantité : 6
6 x tomates ajouté(s).

=== LISTE DE COURSES ===
1. Ajouter un article
2. Retirer un article
3. Afficher la liste
4. Vider la liste
5. Quitter
Ton choix : 3
- pain : 3
- tomates : 6
Total : 9 article(s)

=== LISTE DE COURSES ===
1. Ajouter un article
2. Retirer un article
3. Afficher la liste
4. Vider la liste
5. Quitter
Ton choix : 2
Article à retirer : beurre
beurre n'est pas dans la liste.

=== LISTE DE COURSES ===
1. Ajouter un article
2. Retirer un article
3. Afficher la liste
4. Vider la liste
5. Quitter
Ton choix : 7
Choix invalide, tape un nombre entre 1 et 5.

=== LISTE DE COURSES ===
1. Ajouter un article
2. Retirer un article
3. Afficher la liste
4. Vider la liste
5. Quitter
Ton choix : 5
Bonnes courses, à bientôt !
**Points clés de la solution :** - `while True` + `break` est le schéma classique d'un menu : on boucle « pour toujours » jusqu'à ce que l'utilisateur choisisse de quitter. - `.strip().lower()` normalise les noms : « Pain », « pain » et « PAIN » désignent le même article. - `courses.get(article, 0) + quantite` gère d'un coup le cas « nouvel article » et le cas « article déjà présent ». - `saisie.isdigit()` est vérifié **avant** `int(saisie)` : si la saisie contient des lettres, `int()` provoquerait une erreur. Grâce à l'évaluation paresseuse de `or` (chapitre 02), `int(saisie)` n'est jamais appelé si `isdigit()` est faux. Au [chapitre 08](08-erreurs-et-debogage.md), tu verras une autre approche avec `try/except`. - `sorted(courses)` renvoie les clés triées sans modifier le dictionnaire.

Récapitulatif

  • Une liste [a, b, c] est ordonnée et modifiable ; on accède aux éléments par indice à partir de 0 (-1 pour le dernier).
  • Méthodes essentielles : append, insert, extend, remove, pop, sort, reverse, index, count, copy, clear.
  • Fonctions utiles : len, min, max, sum, sorted, et le test in.
  • Le slicing liste[debut:fin:pas] extrait une portion (fin exclue).
  • b = a ne copie pas : les deux noms désignent la même liste. Pour copier : a.copy(), list(a) ou a[:] (et copy.deepcopy pour les listes imbriquées).
  • Ne modifie jamais une collection pendant que tu la parcours : construis-en une nouvelle.
  • Un tuple (a, b) est immuable ; on le déballe avec x, y = point. Un tuple à un élément s'écrit (x,).
  • Un dictionnaire {cle: valeur} permet de retrouver une valeur par sa clé ; get(cle, defaut) évite les KeyError, items() permet de parcourir les paires.
  • Un ensemble {a, b} ne contient pas de doublons ; il se crée vide avec set() et supporte |, &, -, ^.

Checklist de validation

  • Je sais créer une liste, lire liste[0] et liste[-1], et la modifier.
  • Je connais la différence entre append et extend, entre sort() et sorted().
  • Je sais expliquer pourquoi b = a ne copie pas une liste, et comment faire une vraie copie.
  • Je sais déballer un tuple et échanger deux variables en une ligne.
  • Je sais compter des occurrences avec un dictionnaire et get.
  • Je sais parcourir un dictionnaire avec items().
  • Je sais supprimer les doublons d'une liste et trouver les éléments communs à deux listes.
  • J'ai terminé le mini-projet « liste de courses ».

← Chapitre précédent | Sommaire | Chapitre suivant →