2.1 · Listes, tuples, dictionnaires & ensembles

Niveau 2 · Structures de données & POO

2.1Listes, tuples, dictionnaires & ensembles

Objectif : Choisir la bonne structure de données et la manipuler efficacement.
Temps estimé : 16 min · lecture autonome · Si vous n'avez que 5 min : lisez « L'essentiel en 30s » + le schéma.

L'essentiel en 30 secondes

  • Quatre conteneurs, quatre usages : liste ordonnée modifiable, tuple ordonné figé, dict par clés, set de valeurs uniques — le bon conteneur au bon usage.
  • Le dictionnaire est la structure reine : liste de dicts = 90 % des données réelles (API, CSV, JSON) — accès par clé, .get() sans KeyError.
  • Copier n'est pas assigner : = partage, .copy() copie à plat, deepcopy() copie tout — vérifier par test sur l'imbriqué.

Mini-cas filé — Nabil et les ventes d'Alger

Nabil, data analyst junior à Alger, reçoit un export de ventes : une liste de lignes avec des doublons, des clients à retrouver par code, des coordonnées GPS figées et des catégories à dédoublonner. Sa première version cherche les clients en parcourant une liste — lent et fragile. Il reconstruit : les ventes en liste de dictionnaires (une clé par champ), l'annuaire clients en dict indexé par code avec .get(), les coordonnées en tuples immuables, les catégories en set. Ce même jeu de données revient en 2.2 (rapports formatés en f-strings, sauvegarde JSON), 2.3 (objets Vente et Client) et 2.4 (fichier manquant géré proprement).

Fil conducteur du niveau 2 : ce même jeu de données de Nabil revient dans le cours, le schéma, le script et le défi 48h — et continue en 2.2, 2.3 et 2.4.

1. Quatre conteneurs, quatre tempéraments

Les vraies applications manipulent des collections : une liste de ventes, un annuaire de clients, des catégories. Python propose quatre structures fondamentales. La liste ([]) est ordonnée et modifiable — le couteau suisse : ajout avec append(), accès par index dès 0, dernier élément en [-1]. Le tuple (()) est ordonné mais immuable : parfait pour des données figées (coordonnées GPS, paire figée) et utilisable comme clé de dictionnaire. Le dictionnaire ({}) associe des clés à des valeurs — le carnet d'adresses : accès par clé, pas par position. L'ensemble (set()) ne garde que des valeurs uniques, sans ordre : idéal pour dédoublonner et tester l'appartenance avec in. Nabil l'applique : ventes en liste, coordonnées en tuple, clients en dict, catégories en set.

2. Le dictionnaire, structure reine

Le dictionnaire mérite son statut : client = {"nom": "Nabil", "wilaya": "Alger"}, accès client["nom"], et surtout client.get("tel", "?") qui rend une valeur par défaut sans KeyError. Une liste de dictionnaires représente 90 % des données réelles (lignes d'API, de CSV, de JSON) : chaque ligne est un dict, l'ensemble est une liste. On parcourt avec .keys() (clés), .values() (valeurs) et .items() (paires : for cle, valeur in d.items()). Chercher par clé dans un dict est direct ; chercher par clé en parcourant une liste est lent et fragile — la règle de Nabil : dès qu'on cherche par identifiant, on indexe en dict.

3. Copier sans partager (le retour des étiquettes)

En Python, b = a ne copie pas : les deux noms pointent vers le même objet — modifier l'un modifie l'autre. b = a.copy() copie en surface (les objets imbriqués restent partagés), copy.deepcopy() copie tout, y compris l'imbriqué. Règle : copy() pour une structure plate, deepcopy() pour de l'imbriqué — et un petit test pour vérifier (id() ou modification d'essai). Nabil s'en souvient : sa « copie » du tableau mensuel modifiait l'original jusqu'à ce qu'il passe en deepcopy pour les lignes imbriquées.

Erreur d'interprétation fréquente : « Un dict et une liste, c'est pareil : je peux tout faire avec des listes ». Non. Une liste cherche par POSITION (parcours, lent, fragile dès qu'on cherche par identifiant) ; un dict cherche par CLÉ (direct, lisible, sûr avec .get()). Parcourir une liste de clients pour retrouver un code à chaque fois, c'est le programme qui ralentit et se complique dès que les données grandissent. La règle de Nabil : collection à parcourir dans l'ordre = liste ; accès par identifiant = dict indexé par cet identifiant.
Bien conteneuriser Le bon conteneur au bon usage Liste (ordre) Ordonnée,modifiable — append, indexDict (clés) Accès par clé —.get(), .items()Set (uniques) Dédupliquer,tester avec in
Le bon conteneur au bon usage
Version texte du schéma (SEO / lecteurs d'écran / traduction) : 1) Nabil range ses ventes en LISTE (ordre, append, index dès 0) et ses coordonnées GPS en TUPLE figé (immuable, utilisable en clé). 2) Il indexe ses clients en DICT par code (accès direct, .get() avec défaut, parcours en .items()) au lieu de parcourir une liste — la liste de dicts couvre API, CSV et JSON. 3) Il dédoublonne les catégories en SET et teste l'appartenance avec in. 4) Il COPIE en conscience : = partage, .copy() à plat, deepcopy() pour l'imbriqué, vérifié par test. Leçon : le bon conteneur au bon usage, et copier sans partager.

🔎 Exemple concret — 🔎 Exemple concret — Dédupliquer et compter les wilayas de Nabil

Le set déduplique, la compréhension compte, comme sur les ventes de Nabil :

ventes = ["Alger", "Oran", "Alger", "Blida", "Oran", "Alger"]
uniques = set(ventes)
comptes = {w: ventes.count(w) for w in uniques}
print(comptes)  # {'Alger': 3, 'Oran': 2, 'Blida': 1}

Lecture : set() déduplique, la compréhension en dict compte chaque wilaya.

Leçon : tuple pour les clés composites immuables, dict pour l'accès par clé.

Vocabulaire de la section

Liste (list)
Collection ordonnée et modifiable, entre crochets : [1, 2, 3]. Ajout avec append(), accès par index dès 0, [-1] pour le dernier.

Exemple simple : Nabil range ses ventes dans une liste parcourue avec for.

Ce que ce n'est pas : une structure d'accès par clé.

Réflexe : ordre à parcourir = liste.

Tuple
Collection ordonnée mais IMMUABLE (non modifiable), entre parenthèses : (lat, lon). Protège les données figées, utilisable comme clé de dict.

Exemple simple : Les coordonnées GPS de Nabil sont des tuples.

Ce que ce n'est pas : une liste qu'on modifie au fil du programme.

Réflexe : donnée figée = tuple.

Dictionnaire (dict)
Ensemble de paires clé→valeur, entre accolades : {"nom": "Nabil"}. Accès par clé, .get() avec défaut sans KeyError, parcours en .items().

Exemple simple : Nabil retrouve un client par son code via annuaire[code].

Ce que ce n'est pas : une liste parcourue pour chercher par identifiant.

Réflexe : chercher par identifiant = dict.

Ensemble (set)
Collection non ordonnée de valeurs UNIQUES : {1, 2, 3}. Idéale pour dédoublonner (set(liste)) et tester l'appartenance avec in.

Exemple simple : Nabil dédoublonne les catégories avec set(categories).

Ce que ce n'est pas : une liste avec doublons pour tester l'appartenance.

Réflexe : uniques ou appartenance = set.

Copie (assignation / copy / deepcopy)
= partage le même objet ; .copy() copie en surface (imbriqué partagé) ; copy.deepcopy() copie tout, y compris l'imbriqué.

Exemple simple : Nabil duplique son tableau mensuel imbriqué avec deepcopy.

Ce que ce n'est pas : une assignation crue copiée.

Réflexe : imbriqué à dupliquer = deepcopy + test.

En pratique — En pratique — Choisir et manipuler la bonne structure

  1. Créez une liste de ventes, ajoutez (append), retirez (remove), triez (sort) et accédez par index. Parcourez-la avec une boucle for.
  2. Créez un dictionnaire « client » (nom, email, wilaya). Accédez par clé, ajoutez une clé, utilisez .get() avec défaut, parcourez avec .items().
  3. Créez un tuple de coordonnées (immuable) et tentez de le modifier : constatez l'erreur. Concluez sur l'intérêt de l'immuabilité.
  4. Dédoublonnez une liste avec set() puis comptez avec une compréhension en dict. Vérifiez la différence entre = et .copy() sur une liste.
Vous choisissez la structure adaptée (liste, tuple, dict, set) et la manipulez : le socle du traitement de données en Python.

Script prêt à dire — justifier le dict à un collègue (30 s)

« Pour les ventes de Nabil, je garde la liste pour l'ordre, mais les clients sont indexés en dict par code : accès direct, .get() avec défaut au lieu de planter, et parcours en .items(). Parcourir la liste à chaque recherche, c'était lent et fragile — là c'est lisible et instantané. »

À dire en montrant une recherche par code avant (boucle) et après (dict).

Modèle copier-coller — annuaire indexé et comptage

ventes = [
    {"client": "C01", "wilaya": "Alger", "montant": 1200},
    {"client": "C02", "wilaya": "Oran", "montant": 800},
    {"client": "C01", "wilaya": "Alger", "montant": 450},
]
annuaire = {"C01": {"nom": "Nabil", "wilaya": "Alger"}, "C02": {"nom": "Lina", "wilaya": "Oran"}}
for v in ventes:
    client = annuaire.get(v["client"], {"nom": "inconnu"})
    print(client["nom"], "->", v["montant"])
wilayas = [v["wilaya"] for v in ventes]
print({w: wilayas.count(w) for w in set(wilayas)})

Export à dédoublonner

Les ventes de Nabil arrivent en double : set() sur les identifiants, puis liste de dicts nettoyée.

Annuaire par code

Chaque client a un code unique : dict indexé par code, .get() avec défaut pour les codes inconnus.

Coordonnées figées

Les points de livraison sont des tuples (lat, lon) : immuables, utilisables comme clés de dict.

Tableau mensuel à dupliquer

Le récapitulatif imbriqué se duplique avec deepcopy : l'original d'Alger reste intact.

✓ Bonnes pratiques

  • Indexer en dict dès qu'on cherche par identifiant (jamais de parcours de liste).
  • Utiliser .get() avec défaut et .items() pour parcourir proprement.
  • Copier en conscience : = partage, copy() à plat, deepcopy() pour l'imbriqué.

✗ Pièges fréquents

  • Chercher par clé en parcourant une liste (lent et fragile).
  • Utiliser dict[cle] sans .get() et subir un KeyError.
  • Croire que = copie la structure (il partage le même objet).

Points clés à retenir

  • Liste = ordonnée + modifiable ; tuple = ordonnée + figée (immuable).
  • Dictionnaire = paires clé→valeur (l'annuaire), omniprésent (JSON, config) ; .get() évite le KeyError.
  • Ensemble = valeurs uniques, parfait pour dédoublonner et tester avec in.
  • Les index commencent à 0 ; [-1] désigne le dernier ; = partage, copy()/deepcopy() copient.

Défi 48h — appliquez demain

Prenez un vrai export (celui de Nabil ou le vôtre) : rangez-le en liste de dicts, indexez un annuaire en dict par code avec .get(), dédoublonnez une colonne avec set(), et prouvez la différence = vs .copy() sur un cas imbriqué.

Checklist sortie de section
  • Je peux choisir la bonne structure (liste, tuple, dict, set) avec un exemple de chacun.
  • Je sais indexer en dict, utiliser .get() et parcourir en .items().
  • J'ai vérifié la différence entre assignation, copy() et deepcopy().
Auto-évaluation 1-5 : est-ce que je choisis le bon conteneur au bon usage ?est-ce que je copie sans partager par accident ?

Questions fréquentes

Quand utiliser un tuple plutôt qu'une liste ?

Utilisez un tuple pour des données qui ne doivent pas changer (coordonnées GPS, date, une paire figée). L'immuabilité protège contre les modifications accidentelles, permet d'utiliser le tuple comme clé de dictionnaire, et est légèrement plus rapide. Tout ce qui évolue (ajouts, retraits, tris) reste du domaine de la liste.

Comment parcourir un dictionnaire ?

Utilisez .items() pour obtenir les paires : « for cle, valeur in mon_dict.items() ». Vous pouvez aussi parcourir seulement les clés (.keys(), ou directement for cle in mon_dict) ou les valeurs (.values()). Pour un accès sûr, préférez .get(cle, defaut) à dict[cle] quand la clé peut manquer.

Vérifiez votre compréhension

Quelle est la différence entre une liste et un tuple ?

Ressources supplémentaires (optionnel, pour aller plus loin)

Examen du niveau 2Questions inédites et durcies pour valider vos acquis