3-02. Structures de données en Python
Dans ce chapitre, nous allons découvrir les trois structures de données fondamentales de Python : les p-uplets (tuple), les tableaux (list) et les dictionnaires (dict).
Les tuples : des données figées
- Définir et manipuler des p-uplets (tuples).
- Accéder aux éléments par indexation.
- Comprendre le principe d’immuabilité d’un tuple.
- Écrire une fonction renvoyant un p-uplet de valeurs.
Créer un tuple
En Python, on déclare un tuple en séparant ses éléments par des virgules, délimités par des parenthèses ( ) :
point = (4, 7) # un couple (2-uplet)
couleur_rgb = (255, 128, 0) # un triplet (3-uplet)
vide = () # un tuple vide
un_seul = (42,) # attention à la virgule pour un singleton !
Un tuple peut mélanger des types variés. Par exemple : personnage = ("Gandalf", 2019, True) regroupe une chaîne de caractères, un entier et un booléen.
En math, un couple $*(x, y)*$ regroupe 2 éléments, un triplet $*(x, y, z)*$ en regroupe 3. Plus généralement, un ensemble ordonné de $*p*$ éléments s’appelle un p-uplet. En informatique et en Python, on utilise le terme anglais tuple.
Indexation : accéder aux éléments
Les éléments d’un tuple sont ordonnés et repérés par un index entier qui démarre toujours à 0.
| Index | 0 | 1 | 2 |
|---|---|---|---|
| Élément | "Gandalf" |
2019 |
True |
| Syntaxe | perso[0] |
perso[1] |
perso[2] |
On utilise la fonction len() pour connaître le nombre d’éléments dans un tuple.
perso = ("Gandalf", 2019, True)
print(len(perso)) # Affiche 3
print(perso[0]) # Affiche "Gandalf"
print(perso[2]) # Affiche True
🖐️ Attention à l’erreur IndexError !
Si vous essayez d’accéder à perso[3] alors que le tuple n’a que 3 éléments (index 0, 1 et 2), Python lève une erreur : IndexError: tuple index out of range.
La propriété fondamentale : l’immuabilité
Un tuple est immuable (on utilise souvent le terme anglophone immutable) : une fois créé en mémoire, il est strictement impossible de modifier, ajouter ou supprimer un élément.
point = (10, 25)
point[0] = 12 # ❌ ERREUR : TypeError: 'tuple' object does not support item assignment
Mais pourquoi e˟ s’embêter avec une structure qu’on ne peut même pas modifier ? 🤔
L’immuabilité est une sécurité formidable pour le développeur ! Quand vous passez des coordonnées géographiques ou des constantes de configuration à une fonction, vous avez la certitude absolue qu’aucune ligne de code ne pourra corrompre ou altérer ces valeurs par accident.
Déballage et retours multiples de fonctions
Le déballage (unpacking)
Python permet d’extraire d’un seul coup les valeurs d’un tuple dans des variables distinctes :
coordonnees = (48.85, 2.35)
latitude, longitude = coordonnees # Déballage
print(f"Lat: {latitude}, Long: {longitude}")
# affiche "Lat: 48.85, Long: 2.35"
Renvoyer plusieurs valeurs dans une fonction
Au chapitre 1-01, vous avez vu qu’une fonction termine son exécution avec return. Mais comment renvoyer deux ou trois résultats à la fois ? On renvoie simplement un tuple !
def division_euclidienne(a, b):
quotient = a // b
reste = a % b
return (quotient, reste) # Renvoie un 2-uplet
q, r = division_euclidienne(17, 5)
print(f"17 = 5 × {q} + {r}") # Affiche : 17 = 5 × 3 + 2
Applications sur les tuples
1. On définit pixel = (255, 128, 64).
- Que renvoie
pixel[1]? - Que provoque l’instruction
pixel[0] = 0? Pourquoi ?
2. Écrivez une fonction geometrie_rectangle(largeur, hauteur) qui prend en paramètres les dimensions d’un rectangle et renvoie un tuple contenant son périmètre et son aire.
3. Comment appeler cette fonction pour stocker directement le périmètre dans une variable p et l’aire dans une variable a pour un rectangle de largeur 5 et hauteur 3 ?
Correction
1.
pixel[1]renvoie128(l’élément au rang 1).- L’instruction provoque une erreur
TypeErrorcar les tuples sont immuables (on ne peut pas modifier un élément existant).
2. Fonction de calcul géométrique :
def geometrie_rectangle(largeur, hauteur):
perimetre = 2 * (largeur + hauteur)
aire = largeur * hauteur
return (perimetre, aire)
3. Déballage direct :
p, a = geometrie_rectangle(5, 3)
Les listes : des séquences modifiables
- Lire et modifier les éléments d’une liste grâce à leurs index.
- Itérer sur une liste (par élément ou par indice).
- Construire une liste par répétition et par compréhension.
- Manipuler des tableaux à deux dimensions (matrices) avec la notation
a[i][j].
Listes et tableaux en Python
En Python, les termes liste et tableau désignent exactement la même chose. Nous utiliserons donc indifféremment l'un ou l'autre de ces deux mots tout au long de l'année (les nuances théoriques entre ces deux notions seront abordées en classe de terminale).
Une liste (type list) est une suite ordonnée d'éléments délimitée par des crochets [ ], séparés par des virgules :
notes = [14, 18, 9, 12, 16] # Une liste d'entiers
jours = ["Lundi", "Mardi", "Mercredi"] # Une liste de chaînes
vide = [] # Une liste vide (très utile !)
Lecture et modification
Contrairement aux tuples, les tableaux sont mutables : on peut modifier des éléments, en ajouter et en supprimer.
notes = [14, 18, 9, 12]
print(notes[2]) # Affiche 9 (l’élément à l’index 2)
notes[2] = 11 # On remplace 9 par 11
print(notes) # Affiche [14, 18, 11, 12]
notes.append(17) # Ajout d’un élément à la fin
print(notes) # Affiche [14, 18, 11, 12, 17]
n = notes.pop(1) # Supprime et renvoie notes[1] (n = 18)
print(notes) # Affiche [14, 11, 12, 17]
d = notes.pop() # Supprime et renvoie le dernier élément (d = 17)
print(notes) # Affiche [14, 11, 12]
# Si on n’a pas besoin de récupérer l’élément supprimer :
del notes[1] # Supprime l’élément notes[1]
# ou encore :
notes.pop(1) # On ne récupère pas l’élément renvoyé
Modifier une liste
1. Voici une liste des notes d’Alice en NSI : notes = [5, 14, 15]
- Alice reçoit une nouvelle note (20). Ajouter cette note à la liste.
- Le professeur décide de retirer la première note. Il n’a pas besoin de la récupérer. Écrire la ligne de code permettant de faire cela.
2. Soit la liste pile = ["dossier_1", "dossier_2", "dossier_3"]
- Quelle instruction permet d’afficher le dernier dossier ?
- Quelle instruction permet de retirer ce dernier dossier et de le stocker dans une variable
en_cours?
Parcourir une liste
Il existe plusieurs façons de parcourir une liste avec une boucle for :
temperatures = [18.5, 21.0, 19.2]
for temp in temperatures:
# temp prend successivement les valeurs de temperatures
print(f"Température : {temp}°C")
for i in range(len(temperatures)):
# i varie entre 0 et le plus grand indice de temperatures
print(f"Jour {i} : {temperatures[i]}°C")
temperatures[i] += 1.0 # Modifie la valeur
for i, temp in enumerate(temperatures):
print(f"Index {i} : {temp}")
Parcourir une liste (on dit aussi itérer sur une liste) est un savoir faire essentiel ! Vous devez vous approprier ce concept le plus vite possible, on l’utilisera sans cesse par la suite.
Parcourir une liste
Un restaurant propose trois plats : brochettes de poulet, cotis de porc et poisson grillé.
1. Créer une liste avec ce trois plats.
2. Écrire une fonction qui prend une liste de plats en argument et qui affiche dans la console :
"plat n°1 : ..."
"plat n°2 : ..."
etc.
Construire une liste
Par répétition
Pour créer un tableau d’une taille donnée contenant la même valeur initiale partout :
zeros = [0] * 8 # [0, 0, 0, 0, 0, 0, 0, 0] (un octet initialisé à 0 !)
cases = [False] * 5 # [False, False, False, False, False]
Construction par compréhension
La compréhension de tableau est une syntaxe très élégante et condensée propre à Python. Elle permet de construire un nouveau tableau en appliquant une formule mathématique ou un filtre en une seule ligne.
# Méthode classique avec boucle
carres = []
for x in range(6):
carres.append(x**2)
# Méthode PAR COMPRÉHENSION
carres = [x**2 for x in range(6)]
# Résultat : [0, 1, 4, 9, 16, 25]
On peut également y ajouter une condition if de filtrage :
# Ne garder que les nombres pairs :
nombres = [1, 4, 9, 12, 15, 18, 20]
pairs = [n for n in nombres if n % 2 == 0]
# pairs vaut : [4, 12, 18, 20]
Construction par compréhension
1. Soit la liste t = [5, 10, 15, 20]. Créer par compréhension une liste t2 dont chaque élément est le double de ceux de t
2. Écrivez en une seule ligne par compréhension :
- La liste des puissances de 2 de 20 à 27 (les fameux poids du binaire !).
- La liste des entiers de 0 à 50 qui sont divisibles par 5.
Appartenance
Lorsqu’on veut vérifier la présence d’un élément dans un liste, on utilise le mot-clé in, comme pour une chaîne de caractère.
fruits = ["pomme", "banane", "mangue", "pêche"]
# Tester si un élément est présent (renvoie un booléen) :
print("banane" in fruits) # True
print("kiwi" in fruits) # False
Le mot-clé in permettant de tester l’appartenance marche aussi avec les tuples, les chaînes de caractères (on l’a vu dans le chapitre 1-01) et les dictionnaires, qu’on verra un peu plus loin.
Concaténation (fusion de listes)
L’opérateur + entre deux listes (ou plus) renvoie une liste fusionnée contenant les éléments de toutes les listes concaténées.
fruits1 = ["pomme", "banane", "mangue", "pêche"]
fruits2 = ["poire", "cerise"]
courses = fruits1 + fruits2 + ["abricot", "kiwi"]
# courses = ["pomme", "banane", "mangue", "pêche", "poire", "cerise", "abricot", "kiwi"]
Indices négatifs et les slices
Python autorise les indices négatifs : -1 est le dernier élément, -2 l’avant-dernier, etc.
fruits = ["pomme", "banane", "mangue", "pêche"]
print(fruits[-1]) # 'pêche'
print(fruits[-2]) # 'mangue'
Python permet également de prendre seulement des parties de liste (slice = tranche en anglais).
fruits = ["pomme", "banane", "mangue", "pêche"]
print(fruits[1:3]) # de 1 inclus à 3 exclus ["banane", "mangue"]
print(fruits[2:]) # de 2 jusqu’à la fin
print(fruits[:2]) # du début jusqu’à 2 exclus
print(fruits[::-1]) # inverse l’ordre des éléments
Ce qu’on vient de voir à propos des indices est aussi valable pour les tuples et pour les chaînes de caractères !
Slices et fusion de listes
Soient les deux listes suivantes :
liste1 = [5, 3, 7, 8]
liste2 = [11, 14, 16, 19]
Écrire le code permettant d’obtenir une nouvelle liste qui est la fusion des deux premiers éléments de la première liste et des deux derniers éléments de la deuxième.
Tableaux à deux dimensions (matrices et grilles)
Pour représenter une grille de jeu, un plateau d’échecs ou une image bitmap en nuances de gris, on utilise un tableau de tableaux.
| Col 0 | Col 1 | Col 2 | |
|---|---|---|---|
| Ligne 0 | grille[0][0] |
grille[0][1] |
grille[0][2] |
| Ligne 1 | grille[1][0] |
grille[1][1] |
grille[1][2] |
| Ligne 2 | grille[2][0] |
grille[2][1] |
grille[2][2] |
grille = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
print(grille[1][2]) # Ligne index 1, Colonne index 2 ⟶ Affiche 6
grille[0][0] = 99 # On modifie le coin haut gauche
⚠️ Le piège de la copie de référence en 2D !
Pour créer une grille de 3×3 remplie de 0, on pourrait avoir envie d’écrire [[0] * 3] * 3. C’est une erreur ! Cela crée 3 fois la même ligne en mémoire. Si vous modifiez une case, toute la colonne change !
La bonne manière de construire un tableau à 2 dimensions est d’utiliser la compréhension :
# ✅ Grille indépendante de 3 lignes et 3 colonnes :
grille = [[0]*3 for ligne in range(3)]
listes à deux dimensions
1. On modélise un plateau de morpion (3×3) vide avec des espaces " " sur chaque case.
- Créez ce plateau par compréhension dans une variable
plateau. - Placez un
"X"au centre du plateau. - Placez un
"O"dans le coin inférieur droit.
Correction
Les dictionnaires : des données structurées par clés
- Construire et modifier un dictionnaire.
- Modéliser un enregistrement.
- Itérer sur un dictionnaire avec
keys(),values()etitems(). - Tester l’appartenance d’une clé avec l’opérateur
in.
Associer une étiquette à une valeur
Dans une liste, on accède aux éléments par leur index (0, 1, 2…). Mais on préfère souvent retrouver une information par son nom (par exemple « taille », « prix », « score »).
Un dictionnaire (type dict en Python) stocke des associations clé : valeur. Il se déclare avec des accolades { } :
# Dictionnaire modélisant un joueur
joueur = {
"pseudo": "PixelKnight",
"niveau": 12,
"classe": "Guerrier",
"pv": 85
}
🖐️ Règles sur les clés et les valeurs :
- Les clés doivent être uniques et immuables (le plus souvent des chaînes de caractères
strou des entiersint). - Les valeurs peuvent être de n’importe quel type (entiers, booléens, tuples, listes, ou même d’autres dictionnaires !).
Lire, modifier et ajouter des éléments
L’accès se fait en spécifiant la clé entre crochets : dico[cle].
# 1. Lecture
print(joueur["pseudo"]) # Affiche "PixelKnight"
# 2. Modification d’une valeur existante
joueur["pv"] = 100
# 3. Ajout d'une NOUVELLE clé
joueur["mana"] = 50 La clé n’existait pas, Python la crée automatiquement !
# 4. Suppression
del joueur["classe"]
Sécuriser l'accès et supprimer : in, get() et pop()
Tenter d’accéder à une clé inexistante (ex : joueur["vitesse"]) provoque une erreur bloquante KeyError. Il existe trois manières de gérer cela proprement :
# 1. Vérifier la présence avec 'in' :
if "vitesse" in joueur:
print(joueur["vitesse"])
# 2. Utiliser .get() avec valeur par défaut :
# Renvoie None si la clé n'existe pas, ou la valeur de secours précisée
vit = joueur.get("vitesse", 10) # Renvoie 10 si "vitesse" n'est pas définie
# 3. Supprimer et récupérer une valeur avec .pop() :
niveau = joueur.pop("niveau") # Supprime "niveau" et stocke sa valeur
# (on peut aussi utiliser 'del joueur["niveau"]'
# si on ne veut pas récupérer la valeur)
Les 3 méthodes de parcours d’un dictionnaire
Pour parcourir un dictionnaire avec une boucle for, Python dispose de 3 méthodes fondamentales :
| Méthode | Ce que l’on parcourt | Exemple de syntaxe |
|---|---|---|
.keys() |
Les clés (comportement par défaut) | for cle in d.keys(): (ou for cle in d:) |
.values() |
Uniquement les valeurs | for val in d.values(): |
.items() |
Les couples (clé, valeur) à la fois | for cle, val in d.items(): |
inventaire = {"épée": 1, "potions": 5, "pièces": 120}
# Afficher l'inventaire complet :
for objet, quantite in inventaire.items():
print(f"- {objet} : {quantite}")
Dictionnaires imbriqués (données structurées)
La valeur associée à une clé peut elle-même être un dictionnaire ! C'est la manière standard de représenter des bases de données complexes ou des fichiers de configuration.
eleves = {
"Alice": {"maths": 15, "nsi": 18},
"Bob": {"maths": 12, "nsi": 14}
}
# Accès par clés successives :
print(eleves["Alice"]["nsi"]) # Affiche 18
# Modifier une note :
eleves["Bob"]["maths"] = 13
Applications sur les dictionnaires
1. On considère le dictionnaire suivant : scores = {"Alice": 45, "Bob": 38, "Charlie": 52}.
- Comment augmenter le score de Bob de 10 points ?
- Comment ajouter la joueuse "Diane" avec un score de 60 ?
2. Écrivez une fonction total_points(dico_scores) qui prend en paramètre un dictionnaire de scores et renvoie la somme de tous les scores.
3. Histogramme d’un texte : Écrivez une fonction compter_caracteres(texte) qui prend une chaîne de caractères et renvoie un dictionnaire associant chaque caractère au nombre de fois où il apparaît dans le texte.
Exemple : compter_caracteres("nsi") renvoie {'n': 1, 's': 1, 'i': 1}.
4. On gère un inventaire : stock = {"pommes": 5, "oranges": 2}. Utilisez .get() pour afficher le stock de "bananes", ou afficher 0 si l'article n'est pas en stock.
5. Soit le dictionnaire suivant :
repertoire = {
"Alice": {"classe": "1G1", "options": ["NSI", "Maths"]},
"Bob": {"classe": "1G2", "options": ["NSI", "Physique"]}
}
- Comment accéder à la première option d’Alice ?
- Comment ajouter l'option
"Maths"à la liste des options de Bob avec.append()?
Correction
1. Manipulation élémentaire :
scores["Bob"] += 10
scores["Diane"] = 60
2. Somme des scores :
def total_points(dico_scores):
total = 0
for s in dico_scores.values():
total += s
return total
3. Comptage d’occurrences :
def compter_caracteres(texte):
occ = {}
for c in texte:
if c in occ:
occ[c] += 1
else:
occ[c] = 1
return occ
print(compter_caracteres("bonne chance"))
Synthèse : quelle structure choisir ?
| Structure | Syntaxe | Mutable ? | Mode d’accès | Quand l’utiliser ? |
|---|---|---|---|---|
p-uplet (tuple) |
(a, b, c) |
❌️ Non | Par indice entier : t[0] |
Coordonnées fixes $*(x, y)*$, retours multiples de fonctions, données qu'on ne veut jamais modifier par accident. |
Tableau (list) |
[a, b, c] |
✅ Oui | Par indice entier : t[0] |
Collections ordonnées et homogènes d'éléments, grilles 2D (grille[i][j]), données qui grandissent ou changent. |
Dictionnaire (dict) |
{"k": v} |
✅ Oui | Par clé nommée : d["k"] |
Enregistrements (fiches avec champs nommés), annuaires, inventaires, tables de comptage. |
Mini-Projet : bataille navale
On va créer une version simplifiée du jeu de la bataille navale.
Contexte & principe
Notre version de la bataille navale va utiliser une grille de 5×5 cases. On y placera trois navires : un torpilleur à deux cases et deux sous-marins à une case.
Les cases vides seront noires et les cases occupées par un navire seront vertes.
Étape 1 : codes couleurs et grille vide.
1. Créer un dictionnaire PALETTE qui associe la clé "F" à l’émoji carré noir ⬛, et la clé "N" à l’émoji carré vert 🟩.
PALETTE = {
# à compléter
}
La grille de jeu est initialement vide, donc chacune des 25 cases doit correspondre à un carré noir. Elle correspond donc initialement à ceci :
grille = [
["F", "F", "F", "F", "F"],
["F", "F", "F", "F", "F"],
["F", "F", "F", "F", "F"],
["F", "F", "F", "F", "F"],
["F", "F", "F", "F", "F"],
]
2. Écrivez une fonction creer_grille() qui renvoie par compréhension une grille vide telle que présentée ci-dessus.
3. Écrivez une fonction afficher_grille(grille, palette) qui parcourt la grille ligne par ligne et affiche les émojis correspondants pour faire apparaître le dessin de la grille dans le terminal. Afficher une grille vide doit faire dans le terminal :
⬛⬛⬛⬛⬛
⬛⬛⬛⬛⬛
⬛⬛⬛⬛⬛
⬛⬛⬛⬛⬛
⬛⬛⬛⬛⬛
Étape 2 : colorier une case
Tout au long du jeu, on va avoir besoin de changer la couleur d’une case. Créer la fonction colorier_case(grille, case, couleur, palette) dans laquelle :
grilleest la grille qui contient la case dont il faut changer la couleurcaseest un tuple(x,y)oùxest le numéro de la colonne de la case (entre 1 et 5) etyle numéro de sa ligne (entre 1 et 5 aussi).couleurest la lettre codant la couleur associée (pour l’instant, soit"F", soit"N")palette: le dictionnaire qui associe le code couleur à un carré coloré.
Si le code couleur donné n’existe pas (par exemple si on entre "A"), la fonction doit choisir la couleur du fond ("F").
La fonction n’a pas besoin de renvoyer une nouvelle grille car elle modifie directement la grille existante.
Étape 3 : placer les bateaux
Écrire le code nécessaire pour placer les trois bateaux : le torpilleur à deux cases (horizontal ou vertical) et les deux sous-marins à une case.
Étape 4 : tirer
Écrire la fonction tirer(grille, case) qui colorie la case en rouge si le tir touche un navire, sinon en blanc.
Vous allez devoir compléter le dictionnaire PALETTE avec un carré rouge 🟥 et un carré blanc ⬜.