Définition
Il arrive souvent qu'on ait besoin d'une seule partie d'une liste ou d'une chaîne : les trois premiers résultats d'une recherche, tout sauf le dernier élément, un nom sur deux dans un fichier. Écrire une boucle pour les extraire un par un fonctionne, mais alourdit le code. Le slicing répond directement à ce besoin.
Il découpe une portion d'une séquence et renvoie un nouvel objet, sans toucher à l'original. L'écriture tient en deux nombres séparés par un deux-points, entre crochets : sequence[debut:fin]. Voici ce que ça donne sur une liste de mois :
mois = ["janvier", "février", "mars", "avril", "mai"]
premier_trimestre = mois[0:3]
# ['janvier', 'février', 'mars']Cette écriture s'applique à tout ce qui se parcourt par rang : une liste, un tuple, une chaîne de caractères, un objet range. Un dictionnaire, lui, n'en accepte aucun : il n'a pas de rang, seulement des clés.
La borne de fin reste dehors
Le premier rang est inclus, le second exclu. Cette asymétrie déroute au début, mais elle rend service assez vite : la taille de la tranche vaut exactement fin - debut, et deux tranches qui se rejoignent au même rang reconstituent la séquence entière, sans doublon ni trou. Voici ce principe sur le mot Python, découpé entre les rangs 1 et 4 :
Le calcul suivant le vérifie :
mois[:2] + mois[2:] == mois
# TrueLes deux bornes sont d'ailleurs facultatives. Omettre la première part du début, omettre la seconde va jusqu'à la fin, omettre les deux renvoie la séquence complète. Un rang négatif compte depuis la fin, ce qui dispense de calculer une longueur avec len pour atteindre les derniers éléments. Voici les écritures les plus utiles :
| Écriture | Ce qu'elle renvoie |
|---|---|
mois[2:] | Du troisième élément jusqu'à la fin |
mois[:2] | Les deux premiers éléments |
mois[-2:] | Les deux derniers éléments |
mois[:-1] | Tout sauf le dernier |
mois[::2] | Un élément sur deux |
mois[::-1] | La séquence dans l'ordre inverse |
Le pas est positif, la lecture va de gauche à droite et s'arrête avant la borne de fin, qui est exclue.
Les rangs négatifs comptent depuis la fin. Le découpage ne lève jamais d'erreur, même hors limites : c'est ce qui le distingue de l'accès direct par rang.
Le pas, ce troisième nombre
Un deuxième deux-points introduit le pas : sequence[debut:fin:pas]. Il vaut 1 par défaut et indique de combien de rangs avancer entre deux éléments retenus. Voici ce que ça change sur une liste de dix nombres :
nombres = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
nombres[::3] # [0, 3, 6, 9]
nombres[1:8:2] # [1, 3, 5, 7]Un pas négatif parcourt la séquence à l'envers. C'est de là que vient l'écriture la plus reconnaissable du langage, sequence[::-1], qui renvoie la séquence retournée, sur une chaîne comme sur une liste.
Avec un pas négatif, les bornes se lisent aussi à l'envers : debut doit dépasser fin, pas l'inverse. nombres[2:8:-1] ne renvoie donc pas les rangs 2 à 8 retournés, mais une liste vide, puisqu'on ne peut pas reculer du rang 2 vers le rang 8.
Il ne lève jamais d'erreur de rang
Demander un rang qui n'existe pas à une séquence interrompt normalement le programme : sur une liste de cinq mois, mois[10] lève une IndexError. Une tranche se comporte pourtant autrement.
resultats = []
premiers = resultats[:3]
# [] et non une erreurmois[10:20] sur cinq éléments renvoie tranquillement une liste vide. Le découpage rabote les bornes sur la taille réelle de la séquence au lieu de protester quand elles vont trop loin.
Cette tolérance est une bénédiction pour lire les trois premiers résultats d'une recherche ou les premières lignes d'un fichier. Elle devient un piège quand elle masque un jeu de données vide : une tranche vide ne signale rien. Si la suite du programme suppose au moins un élément, mieux vaut le vérifier explicitement plutôt que compter sur le découpage pour prévenir.
Une copie de surface, et l'affectation par tranche
Sur une liste, le découpage renvoie toujours un nouvel objet : copie = original[:] est l'écriture courte d'une copie complète, sans écrire de boucle.
Cette copie reste superficielle : les éléments ne sont pas dupliqués. Si la liste contient d'autres listes, les deux versions partagent leur contenu, et modifier un élément mutable d'un côté se voit de l'autre.
Sur une chaîne, la question ne se pose pas : le texte est immuable, et le découper produit toujours une valeur indépendante.
À gauche du signe égal, la tranche change de rôle : elle ne lit plus, elle remplace. La liste est modifiée sur place, et le nombre d'éléments insérés n'a pas besoin de correspondre à celui de la tranche retirée :
mois[1:3] = ["printemps"]
# la liste raccourcit d'un élémentLa même logique vaut pour del : del mois[1:3] retire la tranche et referme la liste derrière elle. Ces deux écritures ne valent en revanche que sur les séquences modifiables. La même tentative sur un tuple ou sur une chaîne lève une TypeError.
Questions fréquentes
Pourquoi ma tranche contient-elle un élément de moins que prévu ?
Parce que la borne de fin est exclue. Pour obtenir les rangs 2 à 5 inclus, il faut écrire [2:6] et non [2:5]. Le réflexe qui évite définitivement l'erreur consiste à lire le second nombre comme « on s'arrête avant celui-ci », jamais comme « jusqu'à celui-ci ».
Peut-on utiliser des nombres à virgule dans une tranche ?
Non, les bornes et le pas doivent être des entiers de type int. Un nombre à virgule lève une TypeError même lorsqu'il tombe rond, ce qui surprend après une division : celle-ci renvoie toujours un flottant, et il faut passer par la division entière avec // pour obtenir un rang valable.
Comment découper un dictionnaire ?
Il n'existe pas de découpage sur un dictionnaire, puisque ses clés n'ont pas de rang à désigner. Pour n'en garder qu'une partie, on filtre les clés retenues dans une compréhension de liste appliquée aux couples, ce qui reconstruit un dictionnaire réduit.