Mon Labo Python

Comment trier une liste en python sans tout mélanger dans son code

Trier une liste en Python sans tout mélanger : écran affichant du code de tri appliqué à une liste

Deux commandes qui se ressemblent trait pour trait aboutissent à des résultats opposés : l'une écrase la liste d'origine, l'autre la laisse parfaitement intacte. ma_liste.sort() réorganise la liste sur place et ne renvoie rien du tout ; sorted(ma_liste) vous rend une liste triée toute neuve sans toucher à l'originale. Cette nuance, presque invisible pour qui débute, a suffi à me faire perdre une liste de courses entière un soir où je pensais enfin avoir dompté les listes Python. Depuis que je raconte ici mon auto-formation de python débutante, plusieurs lectrices et lecteurs m'écrivent avec des questions très proches sur ce tri qui tourne mal — voici mes réponses, rassemblées comme on trie enfin des papiers qui traînaient depuis des semaines, une astuce de code à la fois.

Pourquoi ma liste a-t-elle disparu après le tri ?

La question la plus fréquente ressemble à la mienne, le soir où j'ai tapé ma_liste = ma_liste.sort() et vu mon écran afficher None à la place de ma liste de courses. Ce None n'est pas un bug : c'est la valeur que .sort() renvoie toujours, parce qu'elle trie la liste directement en mémoire et ne redonne rien en retour. En réaffectant ce None à ma variable — une simple histoire d'affectation, celle qui décide ce que contient une variable à un instant donné — j'avais remplacé ma jolie liste par du vide.

Gaultier, mon voisin de palier, est du genre à répondre à une question technique par deux autres questions plutôt que par une réponse directe. Quand je suis descendue lui demander pourquoi ma liste avait disparu, il m'a renvoyée sur le palier avec : « tu as regardé ce que la méthode retourne ? Et qu'est-ce que tu fais de ce retour ensuite ? » Pas franchement une réponse, mais de quoi chercher seule.

La documentation officielle, je l'ai ouverte en premier réflexe, mais sans exemple concret sous les yeux pour illustrer mon propre cas, les explications glissaient sur moi comme de l'eau sur une poêle bien encrassée. J'ai fini par abandonner la doc et tester à la main, mon stylo tapotant le bord du cahier de brouillon comme s'il pouvait faire sortir la logique tout seul. Ce soir-là, agacée mais sans refermer l'ordinateur, j'ai cherché cette histoire de None jusqu'au bout au lieu de laisser tomber après deux essais ratés — et c'est resté ma règle depuis : si .sort() ne renvoie rien, c'est qu'elle travaille déjà directement sur l'original, donc jamais de signe égal avec elle.

Carnet de notes avec une liste de livres à trier, posé à côté d'un clavier d'ordinateur pour coder en Python

.sort() contre sorted(), la nuance qui change tout

Le tiroir à couverts explique mieux la différence que n'importe quel terme technique : utiliser .sort(), c'est vider le tiroir sur la table, tout ranger, et le remettre à sa place — le tiroir d'origine a changé pour de bon. Utiliser sorted(ma_liste), c'est plutôt commander un tiroir neuf déjà rangé, pendant que l'ancien reste en bazar exactement où il était. Depuis que j'ai saisi ça, ma règle est simple : je garde sorted() par défaut, et je ne sors .sort() que lorsque je suis certaine de ne plus jamais avoir besoin de l'ordre original.

Un lecteur m'a aussi demandé comment vérifier qu'on n'a rien perdu en triant — je m'aide souvent de comment utiliser la fonction len en python pour compter ses éléments, avant et après le tri, et je jette un œil à quelques index au passage, histoire d'avoir une preuve plutôt qu'une impression. C'est une sécurité toute bête, mais elle m'a évité plus d'une fausse alerte après une journée de travail déjà fatigante.

Pourquoi les majuscules passent-elles avant les minuscules ?

Une liste de prénoms triée qui place « Zoé » avant « albert » a de quoi rendre chèvre n'importe quelle débutante, et c'est exactement ce qui m'est arrivé. Python ne connaît pas l'alphabet comme nous : il compare des étiquettes numériques appelées Unicode, où le « A » majuscule vaut 65 et le « a » minuscule vaut 97. Comme 65 est plus petit que 97, toutes les majuscules se retrouvent devant les minuscules, peu importe ce que dirait un dictionnaire papier. Cette histoire de comparaison touche évidemment les chaînes de caractères en général, pas seulement les prénoms.

La parade tient en un seul ajout : key=str.lower dans l'appel de tri, qui dit à Python de comparer tout le monde comme si c'était écrit en minuscule, sans changer les données d'origine. C'est un peu comme demander à des invités d'enlever leur chapeau avant de les aligner par taille, pour que les plumes ne faussent pas la mesure. Ma règle depuis : dès que je trie des mots plutôt que des nombres, je pose la question des majuscules avant même de lancer le tri.

Trier une liste de dictionnaires réclame une règle sur mesure

Avec une liste de dictionnaires — mes fiches de livres, chacune avec un titre et un nombre de pages — trier par titre ne sert à rien si ce qu'on veut, c'est l'ordre de lecture par longueur. Avant de découvrir l'argument key, j'ai bricolé des conversions un peu partout, en retombant sur mes vieilles galères pour comment convertir un string en int en python sans erreur de type, pour un résultat bancal. L'argument key évite tout ça : on écrit une petite fonction, aussi simple qu'une addition, qui va chercher l'étiquette « nombre de pages » sur chaque fiche et la donne à Python comme critère de tri. Cette fonction ne voit que ce qu'on lui transmet, rien de la cuisine autour — une variable qui existe seulement le temps de son calcul, sans venir fouiller dans le reste du programme.

Pour aller plus vite, on peut aussi importer le module operator et utiliser itemgetter plutôt que d'écrire sa propre fonction, un raccourci que je n'ai adopté que récemment. Quand mes critères se compliquent, j'ajoute parfois une condition toute simple dans cette fonction, un if pour décider quoi faire selon le cas, à condition de respecter l'indentation du bloc, sinon Python refuse tout net.

Gwilherm, un pair autodidacte que j'ai croisé dans un fil de forum sur les boucles for, compare le débogage à une panne mécanique : chercher la pièce défaillante plutôt que maudire la machine entière. C'est devenu mon réflexe quand un tri plante avec un message d'erreur de type — un TypeError qui compare des choses incomparables — j'enveloppe l'essai dans un try/except plutôt que de tout recommencer dans la panique. L'algorithme qui fait tourner ce tri en coulisses s'appelle le Timsort, stable par nature : deux fiches avec le même nombre de pages gardent leur ordre de départ, sans être secouées pour rien.

Ce qui reste flou, et mes petits repères de cuisine

Avant de connaître l'argument key, je m'en sortais avec des boucles for bricolées à la main, beaucoup moins élégantes mais qui faisaient le travail tant bien que mal. Que la liste vienne d'un fichier lu en soirée ou d'une saisie tapée au clavier par un utilisateur, le principe du tri ne change pas d'un poil. Et bonne nouvelle pour qui débute : rien de tout ça ne demande d'installer une bibliothèque avec pip ni de toucher à son installation de Python, c'est du langage de base. L'autre soir, en rentrant du quartier Montferrand, j'ai même essayé de créer un menu interactif en python avec une boucle while sans bloquer pour choisir la méthode de tri d'un simple chiffre au clavier — encore un peu bancal, mais ça avance.

Ce qui me reste flou, c'est comment un tri se comporte sur des listes vraiment énormes, celles qu'on ne pianote pas à la main un soir de semaine. Je n'ai pas encore eu l'occasion de tester ça sérieusement. En attendant, mes listes de courses ont enfin de l'allure, et pour une débutante qui apprend seule le soir, c'est déjà une victoire. Et vous, quelle commande vous a fait perdre des données sans prévenir ?

Articles connexes