Aller au contenu principal

Listes, dictionnaires et tuples

Ce que ce chapitre apporte 5 points
  • Ranger une suite de valeurs dans une liste, atteindre un élément par son indice, et la parcourir.
  • Ajouter, retirer et modifier des éléments ; calculer une somme, un minimum, un maximum.
  • Trier avec sorted() ou .sort(), en sachant lequel modifie la liste.
  • Associer une valeur à une clé dans un dictionnaire, et le parcourir.
  • Employer un tuple pour des valeurs qui vont ensemble et ne doivent plus bouger.

Une variable retient une valeur. Que fait-on quand on en a trente ?

Écrire note1, note2, note3 jusqu'à note30 est possible, et c'est une impasse : impossible d'en faire la moyenne sans écrire trente additions, impossible d'en ajouter une trente et unième sans modifier le programme. Il faut ranger plusieurs valeurs sous un seul nom. C'est l'objet de ce chapitre.

Il présente trois façons de le faire : la liste, qui range des valeurs dans l'ordre, le dictionnaire, qui les range par clé, et le tuple, qui ne se modifie plus. Il suffit pour cela des variables, des conditions et des boucles. Le reste, les tranches, les compréhensions, le texte vu comme une suite de caractères, les copies, les listes de listes et les ensembles, est rangé à part, en fin de chapitre.

Chaque bloc part d'un Python neuf
Les blocs de cette page ne se souviennent pas les uns des autres : les données sont redéfinies à chaque fois. On peut donc modifier et relancer n'importe lequel sans casser les suivants.

La liste : une suite de valeurs

Définition

Une liste est une collection ordonnée et modifiable de valeurs, écrite entre crochets. Elle peut contenir des nombres, du texte, ou un mélange.

main.py
Sortie
>_ Prêt à exécuter…

Quatre valeurs suffisent à voir l'intérêt : la moyenne s'écrit pareil pour quatre notes ou pour trois mille.

Atteindre un élément

Chaque élément a un indice, et les indices commencent à zéro.

main.py
Sortie
>_ Prêt à exécuter…
Trois éléments, indices 0, 1 et 2
Le décalage entre « le premier » et l'indice 0 est la source d'erreur numéro un sur les listes. Une liste de trois éléments a pour dernier indice 2, pas 3.
La règle : le dernier indice vaut toujours len(liste) - 1. Ou plus simplement, liste[-1], qui compte depuis la fin et ne se trompe jamais.

Le bloc suivant est faux exprès.

main.py
Sortie
>_ Prêt à exécuter…

IndexError: list index out of range. Il y a bien trois notes, mais leurs indices sont 0, 1 et 2. Remplacer 3 par 2, ou mieux par -1, puis relancer.

Modifier

Une liste est mutable : on peut changer, ajouter, retirer après coup.

main.py
Sortie
>_ Prêt à exécuter…

append, remove, pop et len suffisent pour presque tout. Il en existe d'autres, insert pour placer à un endroit précis, extend pour ajouter plusieurs éléments, clear pour vider : on les cherchera le jour où l'on en aura besoin.

Deux opérateurs déjà connus changent de sens sur des listes. + en colle deux bout à bout et * en répète une : [1, 2] + [3] donne [1, 2, 3], et [0] * 3 donne [0, 0, 0]. Ils n'additionnent ni ne multiplient rien, ils assemblent, et ils fabriquent une nouvelle liste sans toucher aux anciennes. La seconde écriture sert à préparer une liste de taille connue ; pour une grille, elle cache un piège, montré en fin de chapitre.

Parcourir et calculer

main.py
Sortie
>_ Prêt à exécuter…
Compter, cumuler, filtrer
Le schéma revient partout : on prépare un résultat vide avant la boucle, on le fait grandir dans la boucle, on l'utilise après.
main.py
Sortie
>_ Prêt à exécuter…

Trier

Trier se fait de deux façons, qui ne font pas la même chose. sorted(notes) fabrique une nouvelle liste triée et laisse l'original intact. notes.sort() trie la liste elle-même, sur place, et ne renvoie rien. Les deux acceptent reverse=True pour l'ordre décroissant.

main.py
Sortie
>_ Prêt à exécuter…

Le bloc suivant est faux exprès.

main.py
Sortie
>_ Prêt à exécuter…

None s'affiche, puis TypeError: 'NoneType' object is not subscriptable.

notes = notes.sort() jette la liste
.sort() a bien trié la liste, puis a renvoyé None, la valeur qui signifie « rien ». En rangeant ce retour dans notes, on décolle l'étiquette de la liste triée pour la coller sur None : les données sont perdues.
La règle : notes.sort() seul sur sa ligne, ou triees = sorted(notes). Jamais les deux mélangés.
Trier selon un autre critère que la valeur elle-même, des élèves par leur note par exemple, passe par un paramètre key= qui attend une fonction : le chapitre sur bien écrire une fonction, dans sa section « Passer une fonction en paramètre », y revient.
Vérification rapideon peut se reprendre

1.Quel est le dernier indice valide d'une liste de 5 éléments ?

2.Après l = [3, 1, 2] puis l = l.sort(), que vaut l ?

Le dictionnaire : associer une clé à une valeur

Une liste de notes ne dit pas à qui appartient chaque note. On serait tenté de tenir deux listes en parallèle, prenoms et notes, et de les faire correspondre par leur indice. C'est fragile : il suffit d'en trier une pour tout casser.

Définition

Un dictionnaire associe des clés à des valeurs, entre accolades, avec la syntaxe cle: valeur. On accède à une valeur par sa clé, jamais par un indice.

main.py
Sortie
>_ Prêt à exécuter…

Liste ou dictionnaire : la question à se poser.

Comment vais-je retrouver une valeur ?
Par sa position, ou parce que je veux toutes les parcourir dans l'ordre : c'est une liste.
Par un identifiant, un nom, un code, une date : c'est un dictionnaire.
Le symptôme qui doit alerter : deux listes de même longueur qu'on parcourt avec le même indice. Cela veut presque toujours dire qu'un dictionnaire s'imposait.

Parcourir un dictionnaire

main.py
Sortie
>_ Prêt à exécuter…

.keys() donne les clés, .values() les valeurs, .items() les paires. C'est .items() qu'on emploiera le plus.

La clé qui n'existe pas

main.py
Sortie
>_ Prêt à exécuter…

KeyError: 'Zoe'. Demander une clé absente arrête le programme. Quand on n'est pas sûr, .get() répond sans planter, et accepte une valeur de repli.

main.py
Sortie
>_ Prêt à exécuter…
None n'est pas zéro
notes.get("Zoe") renvoie None, qui signifie « rien », et non 0, qui est une note. Calculer avec None déclenche un TypeError.
Quand la valeur sert à un calcul, toujours donner un repli : notes.get("Zoe", 0). Quand la question est seulement de savoir si la clé existe, in est plus clair qu'une comparaison à None.

Compter des occurrences

Compter les occurrences est l'exercice type du dictionnaire, et celui où l'on se perd le plus vite : la clé change à chaque tour, et la valeur associée aussi. La trace le déroule sur un mot, qu'une boucle for parcourt lettre par lettre comme une liste.

pas 1 / 26
1compte = {}
2for c in "abraca":
3 if c in compte:
4 compte[c] = compte[c] + 1
5 else:
6 compte[c] = 1
7print(compte)

Variables

compte{}
Le programme, exécuté pas à pas. La ligne surlignée est celle qui vient de s'exécuter, et le tableau de droite donne l'état des variables juste après.

Le if c in compte distingue les deux seuls cas possibles : la lettre a déjà été vue, et il faut incrémenter ; ou c'est la première fois, et il faut créer l'entrée à 1. Oublier le else donne une erreur de clé absente au tout premier caractère, ce que la trace montre en s'arrêtant net.

Vérification rapideon peut se reprendre

1.Que renvoie {"x": 1}.get("y") ?

2.Deux listes prenoms et notes de même longueur, parcourues avec le même indice. Que faudrait-il ?

Le tuple : des valeurs qui vont ensemble

Un tuple s'écrit entre parenthèses et ne se modifie plus après sa création. Ce n'est pas une liste au rabais : c'est une garantie.

main.py
Sortie
>_ Prêt à exécuter…

Le bloc suivant est faux exprès : il tente de modifier un tuple.

main.py
Sortie
>_ Prêt à exécuter…

TypeError: 'tuple' object does not support item assignment. Le refus est le point : un tuple sert quand les valeurs vont ensemble et ne doivent pas bouger séparément. Une coordonnée, une couleur, une date, un couple largeur-hauteur.

Le dépaquetage est ce qu'on en fait le plus : x, y = point range chaque valeur sous son nom, et la même écriture échange deux variables sans variable temporaire.

main.py
Sortie
>_ Prêt à exécuter…

Une fonction qui renvoie plusieurs valeurs d'un coup, return mini, maxi, renvoie en fait un tuple, que l'appel dépaquette : le chapitre sur les fonctions le montre dans sa partie « Pour aller plus loin ».

Quelle structure choisir

StructureÉcritureModifiableQuand l'employer
Liste[1, 2, 3]ouiune suite de valeurs, parcourue ou indexée
Dictionnaire{"a": 1}ouiretrouver une valeur par un identifiant
Tuple(3, 7)nondes valeurs fixes qui vont ensemble

Deux autres structures complètent ce tableau en fin de chapitre : la chaîne, du texte qui ne se modifie pas, et l'ensemble, des valeurs distinctes sans ordre.

Pour aller plus loin

Rien de ce qui suit n'est nécessaire pour ranger ses premières données. Ce sont des écritures plus courtes et des pièges qu'on rencontre ensuite, une fois la liste et le dictionnaire bien en main.

La liste est l'équivalent Python du tableau, dont le chapitre d'algorithmique sur les tableaux et leur parcours traite indépendamment du langage. Deux différences comptent, et elles se paient : les indices commencent ici à 0 et non à 1, et une liste Python s'allonge toute seule là où un tableau a une taille fixée.

Découper une tranche

Deux indices séparés par un deux-points prennent un morceau de la liste. La borne de gauche est incluse, celle de droite exclue, exactement comme dans range.

main.py
Sortie
>_ Prêt à exécuter…

L'exclusion de la borne droite paraît arbitraire, et elle a une bonne raison : elle rend les tranches raboutables. notes[:3] et notes[3:] reconstituent exactement la liste, sans doublon ni oubli, et la longueur d'une tranche [a:b] vaut simplement b - a.

Une tranche est une copie
notes[:] est une autre façon d'écrire notes.copy() : découper une liste fabrique une nouvelle liste, et la modifier ne touche pas l'original. Le chapitre sur NumPy montrera que ce n'est pas vrai de tous les objets découpables, et c'est l'une des rares fois où Python change de règle.

Construire une liste en une ligne : la compréhension

Filtrer une liste pour en garder une partie revient si souvent que Python lui donne une écriture dédiée, la compréhension : [n for n in notes if n >= 10]. Elle se lit comme la boucle, dans le même ordre : « les n, pour chaque n de notes, si n >= 10 ». Les quatre lignes du schéma « Compter, cumuler, filtrer », liste vide, for, if, append, tiennent entre deux crochets.

La partie if est facultative. Sans elle, la compréhension transforme chaque élément : [n * 2 for n in notes] double toutes les valeurs. Avec des accolades et un deux-points, elle fabrique un dictionnaire, ici des longueurs de pièces converties de millimètres en mètres.

main.py
Sortie
>_ Prêt à exécuter…

Les deux dernières lignes reprennent le compteur et l'accumulateur. Placée directement dans sum(...), sans crochets, la même écriture fournit les valeurs une à une à sum, sans construire de liste intermédiaire : sum(1 for ...) ajoute 1 pour chaque élément retenu, donc les compte, et sum(n for ...) les additionne.

Une compréhension doit se lire d'un coup d'œil
Elle est faite pour une idée simple : garder, transformer, compter. Dès qu'il faut plusieurs conditions imbriquées, un elif, un affichage ou plusieurs résultats à la fois, la boucle écrite en entier redevient la bonne forme.
Le test est simple : si l'on doit relire la ligne deux fois pour savoir ce qu'elle fait, c'est une boucle qu'il fallait.

Le texte, une suite de caractères

Une chaîne se comporte presque comme une liste de lettres, à une différence près : elle est immuable. On peut la lire et en fabriquer de nouvelles, jamais changer une lettre en place.

main.py
Sortie
>_ Prêt à exécuter…

Les deux méthodes qui servent le plus font le lien avec les listes : split() découpe un texte en liste, join() recolle une liste en texte.

main.py
Sortie
>_ Prêt à exécuter…
Immuable, donc pas de méthode qui modifie
mot.upper() ne change pas mot : il renvoie une nouvelle chaîne. Si on veut garder le résultat, il faut le ranger : mot = mot.upper().
C'est l'inverse des listes, où notes.append(15) modifie la liste elle-même et ne renvoie rien. Cette différence explique la moitié des surprises du chapitre.

Pour chercher dans un texte, in fonctionne comme sur une liste, mais il cherche un morceau de texte : "OK" in ligne. Quelques méthodes complètent ce test : startswith et endswith regardent le début et la fin, find donne la position d'un morceau (ou -1 s'il n'y est pas), count compte ses apparitions, strip retire les espaces et le saut de ligne aux deux bouts, et isdigit dit si le texte n'est fait que de chiffres. Elles servent toutes à nettoyer une ligne de relevé avant de s'en servir.

main.py
Sortie
>_ Prêt à exécuter…
Vérification rapideon peut se reprendre

1.Que vaut [x * 2 for x in [1, 5, 8] if x > 3] ?

2.Que fait mot.upper() sur mot = "python" ?

Deux étiquettes, une seule liste

Rappel du chapitre 2 : une variable est une étiquette collée sur une valeur. Avec les listes, cette image cesse d'être une commodité et devient indispensable.

main.py
Sortie
>_ Prêt à exécuter…
L'affectation ne copie pas
equipe_b = equipe_a ne fabrique pas une deuxième liste : elle colle une deuxième étiquette sur la liste existante. Modifier la liste par l'une ou par l'autre revient au même, puisqu'il n'y en a qu'une.
Pour obtenir une copie indépendante : equipe_b = equipe_a.copy(). Elle suffit tant que la liste contient des nombres ou du texte ; pour une liste de listes, il faudra davantage, comme on le verra plus bas.
Ce comportement ne vaut que pour les valeurs modifiables, listes et dictionnaires. Avec un nombre ou un texte, la question ne se pose pas : on ne peut pas les modifier, donc on ne peut pas surprendre l'autre étiquette.

Le dessin ci-dessous montre la situation telle qu'elle est en mémoire, et non telle qu'on l'imagine. Il n'y a qu'une seule liste, et deux étiquettes pointées dessus.

les nomsles objets en mémoireequipe_aequipe_beffectif["Alice", "Bob", "Charlie"]modifiable42immuable
Après equipe_b = equipe_a puis equipe_b.append("Charlie"). Les deux noms désignent le même objet, et c'est pourquoi la modification se voit des deux côtés.

L'image de la boîte, qui suffit tant qu'on manipule des nombres, cesse ici de fonctionner. Une variable n'est pas une boîte qui contient une valeur : c'est une étiquette collée sur un objet. Deux étiquettes sur le même objet, c'est une situation ordinaire, et la seule façon de la comprendre est de la voir.

main.py
Sortie
>_ Prêt à exécuter…

Les listes de listes

Un tableau de relevés, une ligne par jour et une colonne par capteur, se range dans une liste de listes : chaque élément de la liste extérieure est lui-même une liste, une ligne du tableau. releves[1] donne la deuxième ligne, et releves[1][2] l'élément de rang 2 de cette ligne : on lit d'abord la ligne, puis la colonne.

main.py
Sortie
>_ Prêt à exécuter…

Le parcours complet est exactement les boucles imbriquées du chapitre précédent : la boucle extérieure choisit la ligne, l'intérieure en parcourt toutes les colonnes avant de passer à la suivante.

[[0] * 3] * 3 ne fabrique qu'une seule ligne
Pour créer une grille de zéros, l'écriture [[0] * 3] * 3 est tentante. Elle fabrique une ligne de trois zéros, puis une liste qui porte trois étiquettes vers cette même ligne. Modifier une case modifie donc toute la colonne.
La forme correcte est une compréhension, qui fabrique une ligne neuve à chaque tour : [[0] * 3 for _ in range(3)]. Le nom _ est une variable comme une autre, qu'on emploie par convention quand sa valeur ne sert pas.

Le même mécanisme rattrape .copy(). Elle fabrique une nouvelle liste extérieure, mais y range les mêmes lignes que l'original : c'est une copie superficielle, qui ne descend pas d'un niveau. Pour une liste de listes, ou une liste de dictionnaires, il faut une copie profonde, que fournit copy.deepcopy. La ligne import copy va chercher cet outil dans la bibliothèque livrée avec Python ; le chapitre sur la modularité explique ce mécanisme.

main.py
Sortie
>_ Prêt à exécuter…

Dans la première grille, les trois lignes ont reçu le 1 ; dans la seconde, une seule. Et la copie par .copy() a vu apparaître "A3", parce que sa première ligne est celle de l'original, alors que la copie profonde n'a pas bougé.

Vérification rapideon peut se reprendre

1.Après a = [1, 2], b = a, b.append(3), que vaut a ?

2.Après g = [[0] * 2] * 2 puis g[0][0] = 5, que vaut g ?

3.Pour dupliquer une liste de listes qu'on va modifier, que faut-il ?

Un tuple comme clé de dictionnaire

Une clé de dictionnaire doit être immuable : un tuple convient, une liste non. C'est ce qui permet de repérer une case d'une grille par ses deux coordonnées.

main.py
Sortie
>_ Prêt à exécuter…

L'ensemble : des valeurs sans doublon

Un ensemble retient des valeurs sans doublon et sans ordre. Il répond à deux questions que les autres structures traitent mal : « quelles sont les valeurs distinctes ? » et « celle-ci est-elle dedans ? ».

main.py
Sortie
>_ Prêt à exécuter…

La conversion en ensemble puis retour en liste est la façon d'enlever les doublons en Python. Elle tient en une expression, list(set(valeurs)), au prix de l'ordre, que sorted rétablit si besoin.

Les opérations ensemblistes se lisent comme en mathématiques :

main.py
Sortie
>_ Prêt à exécuter…

Pourquoi le test d'appartenance y est si rapide. Chercher une valeur dans une liste de cent mille éléments oblige à les parcourir jusqu'à la trouver. Dans un ensemble, la réponse arrive en un temps qui ne dépend pas de la taille, parce que la valeur elle-même indique où regarder.
D'où la règle : dès qu'un programme fait beaucoup de if x in collection sur une grande collection dont l'ordre est indifférent, l'ensemble est le bon choix. C'est le même mécanisme que la clé de dictionnaire, et c'est pourquoi un ensemble n'accepte lui aussi que des valeurs immuables.

Vérification rapideon peut se reprendre

1.Pourquoi un tuple peut-il servir de clé de dictionnaire, et pas une liste ?

2.set([3, 1, 3, 2, 1]) donne quoi ?

Mettre en pratique

Le bulletin. À partir du dictionnaire ci-dessous, afficher la moyenne générale avec deux décimales, puis le nom du meilleur élève.

main.py
Sortie
>_ Prêt à exécuter…
Afficher la solution
main.py
Sortie
>_ Prêt à exécuter…

La moyenne se lit directement sur les valeurs : sum(notes.values()).

Pour le maximum, on emploie le schéma « préparer, parcourir, comparer » : une variable qui retient le meilleur trouvé jusqu'ici, initialisée à une valeur que tout dépassera. Python offre un raccourci, max(notes, key=notes.get), mais la boucle explicite est ce qu'on attend de soi en devoir, et c'est elle qui se transpose à tous les autres problèmes.

Chapitre révisé en octobre 2026 · Une erreur dans ce chapitre ?

Dans la bibliothèque d'exercices