Pourquoi on compte à partir de 0#
La question que tout le monde se pose en silence : pourquoi maListe[0] est le premier élément, et pas maListe[1] ? Ce n'est pas un caprice d'informaticien. L'indice n'est pas un numéro d'ordre (« le 1er, le 2e... »), c'est un décalage : « de combien de cases faut-il avancer depuis le début ? »
Le tout premier élément est au début même : on n'avance pas du tout, décalage 0. Le suivant est une case plus loin, décalage 1. Vu comme ça, partir de 0 devient la chose la plus naturelle du monde.
L'indice, c'est un décalage depuis le début
Voici une liste de 6 températures. Choisis un indice : la case s'allume, et on lit l'indice comme « avance de tant de cases ».
Petit bonus qui ressort de là : maListe[-1] donne le dernier élément. L'indice négatif compte « à reculons depuis la fin » - bien pratique quand on ne connaît pas la longueur.
Tout collé, ou relié par des chaînons ?#
On imagine souvent une liste comme une rangée de cases bien collées les unes aux autres en mémoire. C'est presque vrai pour une list Python - mais avec une nuance qui change tout, et qui ouvre la porte à une autre façon de ranger les données : la liste chaînée.
1. Le tableau contigu (la list Python)
Une list Python range côte à côte, en mémoire, non pas les objets eux-mêmes, mais une rangée d'adresses (des pointeurs) qui disent où chaque objet se trouve. Cette rangée d'adresses, elle, est bel et bien contiguë. Conséquence magique : pour atteindre l'élément k, l'ordinateur calcule début + k × (taille d'une case) et y saute directement, sans regarder les autres. Un seul bond, quel que soit k. C'est ce qu'on appelle un accès en temps constant.
2. La liste chaînée (des blocs reliés)
Autre idée : ne rien coller du tout. Chaque valeur vit dans son propre petit bloc, posé n'importe où en mémoire, et chaque bloc garde l'adresse du bloc suivant. Pour trouver le k-ième, pas de calcul magique : il faut suivre la chaîne, maillon après maillon, en partant du début. Plus k est grand, plus on marche longtemps.
Alors pourquoi s'embêter avec une chaîne plus lente à lire ? Parce qu'elle est imbattable pour insérer au milieu : on débranche deux chaînons, on en rebranche un, et c'est fini - sans décaler tout le reste. Dans un tableau contigu, insérer au début oblige à tout pousser d'un cran vers la droite.
Va chercher l'élément numéro k - et compte les sauts
Même donnée, deux rangements. Demande un indice : à gauche le tableau saute droit au but (1 bond) ; à droite la chaîne se parcourt maillon par maillon (k+1 bonds). Regarde le compteur.
Tableau contigu (accès direct)
Liste chaînée (maillon par maillon)
| Opération | Tableau contigu (list) | Liste chaînée |
|---|---|---|
Lire l'élément k | 1 bond, direct (constant) | k+1 bonds (proportionnel à k) |
| Ajouter à la fin | Très rapide en général | Rapide |
| Insérer au début / au milieu | Lent : il faut tout décaler | Rapide : on rebranche 2 chaînons |
| En mémoire | Adresses collées côte à côte | Blocs éparpillés, reliés par pointeurs |
En Python, la list est le tableau contigu. La liste chaînée existe aussi : collections.deque s'en inspire pour être ultra-rapide à ajouter/retirer aux deux bouts. Le bon outil dépend de ce qu'on fait le plus souvent : lire au hasard, ou insérer partout.
Deux noms, une seule boîte#
Voici le piège qui surprend absolument tout le monde un jour. On écrit deuxiemeListe = premiereListe en croyant faire une copie. Faux : on a juste collé une deuxième étiquette sur la même boîte. Modifier l'une, c'est modifier l'autre - puisque c'est la même chose.
Essaie : ajoute via la 2e variable, regarde la 1re
Pour obtenir une vraie copie indépendante, on le demande explicitement : deuxiemeListe = premiereListe.copy() (ou list(premiereListe), ou premiereListe[:]). Là, deux boîtes distinctes : toucher l'une laisse l'autre tranquille. Bascule le mode dans la démo pour voir les deux boîtes se séparer.
La famille de la liste#
La liste a des cousins qui se manipulent presque pareil, mais avec un caractère différent. Et une révélation : la chaîne de caractères (str) est elle aussi une suite indexable - on peut piocher dedans avec des crochets, exactement comme dans une liste.
Une chaîne de texte s'indexe comme une liste
| Type | Écriture | Son caractère |
|---|---|---|
list | [10, 20, 30] | Modifiable, ordonnée, doublons permis |
tuple | (10, 20, 30) | Figée une fois créée (on ne peut plus la changer) |
set | {10, 20, 30} | Sans doublons, sans ordre, sans indices |
str | "bonjour" | Suite de caractères, indexable, mais figée |
Le bon réflexe : liste quand le contenu évolue, tuple pour un paquet figé (des coordonnées (x, y)), set quand seule compte la présence sans répétition (« quelles valeurs uniques ? »).
La liste, c'est ton premier vecteur de données#
En sciences, une liste n'est pas qu'un objet de programmation : c'est la forme naturelle d'un jeu de mesures. Une série de températures relevées d'heure en heure, un signal échantillonné, les positions successives d'un mobile - tout ça, c'est une liste de nombres, c'est-à-dire un vecteur.
temperatures = [18.2, 19.5, 21.0, 22.7, 21.4, 19.8] maximum = max(temperatures) moyenne = sum(temperatures) / len(temperatures)Tout ce qu'on a vu prend alors un sens concret : l'indice repère l'instant de la mesure, l'accès direct permet de sauter à la 1000e mesure sans relire les 999 autres, et la copie évite d'abîmer ses données brutes par accident.
Et il y a une seconde raison, plus surprenante, qui fait de ce vecteur de mesures l'outil de toute la science moderne : sur un tableau, on peut calculer sur toutes les cases à la fois - et ta machine cache des parties spéciales taillées exactement pour ça.
Une seule instruction, des milliers de nombres#
Pour doubler toutes les mesures d'une list Python, il faut une boucle qui passe case par case. Avec un tableau NumPy, on écrit juste mesures * 2 et tout se calcule « d'un coup ». On dit souvent que c'est rapide « parce que c'est écrit en C » - vrai, mais la raison profonde est matérielle, et bien plus belle.
Quand chaque case subit la même opération, le processeur peut la lancer sur plusieurs nombres en même temps. Ce principe porte un nom : SIMD - « une seule instruction, plusieurs données » (Single Instruction, Multiple Data). C'est la clé de tout ce qui suit.
1. Dans le processeur (CPU) : les voies SIMD
Ton processeur ne sait pas seulement multiplier deux nombres. Il possède des registres larges capables de charger 8, 16, parfois davantage de nombres et de les multiplier tous dans un seul battement d'horloge. NumPy confie ton tableau à ces voies. Une boucle Python, elle, traite un objet à la fois - emballé puis déballé à chaque tour - et laisse ces voies inutilisées.
2. Sur la carte graphique (GPU) : une armée de petits cœurs
Une carte graphique a été inventée pour colorier des millions de pixels en parallèle. Pour ça, elle n'aligne pas 8 unités de calcul, mais des milliers de petits cœurs. Or « appliquer la même formule à un immense tableau », c'est exactement la même forme de travail que colorier des pixels. Les scientifiques ont donc détourné le GPU pour le calcul sur tableaux : avec CuPy, PyTorch ou JAX, le même tableau * 2 s'étale sur des milliers de cœurs à la fois.
Les GPU récents vont encore plus loin avec des tensor cores : des unités ultra-spécialisées qui avalent une multiplication de matrices entière en un éclair. C'est le moteur matériel derrière l'intelligence artificielle d'aujourd'hui - et ça commence par ce simple geste : calculer sur tout un tableau d'un coup.
Même calcul, trois matériels : qui finit en premier ?
32 nombres à doubler. La boucle en traite 1 par battement d'horloge ; les voies SIMD du processeur, 8 ; le GPU, presque tous d'un coup. Lance et compte les « tics ».
Façon de calculer mesures * 2 | Combien de nombres à la fois | Partie du matériel |
|---|---|---|
| Boucle Python | 1 (un par tour) | Un seul cœur, une opération à la fois |
| NumPy (tableau) | 8, 16, parfois plus | Les voies SIMD du processeur |
| CuPy / PyTorch / JAX | Des milliers | Les cœurs du GPU (+ tensor cores) |
À ne pas confondre : NumPy tout seul calcule sur le processeur (il exploite déjà le SIMD, sans GPU). Pour faire travailler le GPU, on change d'outil - cupy, torch, jax - mais le code reste presque identique : c'est la grande force de la pensée « tableau ». On a effleuré la lenteur des boucles côté en savoir plus des boucles ; ici, on a vu quel matériel rend l'autre voie si rapide.
Le prochain voyage#
Tant qu'on aligne des nombres sur une seule rangée, on reste dans le vecteur. Mais imagine que chaque mesure devienne elle-même une petite liste - température et humidité et pression au même instant. On empilerait des rangées les unes sous les autres... et on tiendrait un vrai tableau à deux dimensions - justement ce que les milliers de cœurs d'un GPU adorent mâcher en parallèle.
Mais ça, c'est pour une prochaine fois. Pour l'instant, savoure d'avoir compris ce qui se cache derrière un simple crochet - et pourquoi tableau * 2 peut réveiller des milliers de cœurs à la fois. 🚀