Python : Les Comprehension Lists

Apprendre un langage ne se limite pas à apprendre une syntaxe. Un langage a ses idiomes, ses structures, ses particularités. Ainsi, Python propose des outils qui vont permettre de se faciliter la vie pour certaines choses. Les créateurs du langage ont délibérément choisi de proposer des outils simplifiant les tâches redondantes, celles que l’on fait régulièrement.

Ainsi, il y a en programmation quelque chose que nous faisons régulièrement avec les listes (les collections de manières générale) :

  • Rechercher un élément
  • Transformer les éléments de la liste
  • Filtrer la liste, c’est à dire supprimer les éléments qui ne nous intéressent pas.

Dans ce billet, je vais vous présenter un outil qui facilite la vie pour les deux derniers usages : transformer et filtrer une liste. Cet outil, c’est les comprehension lists ou, en Français, listes en intension (non, il n’y a pas de faute d’orthographe).

Transformer une liste

Transformer une liste, c’est à partir d’une liste créer une nouvelle liste en transformant la donnée d’origine. Prenons un exemple.

La liste suivante me permet de gérer les films Harry Potter. Chaque film est représenté par une liste comportant dans l’ordre le titre du film, sa durée en minutes et un booléen indiquant si je l’ai vu ou non.

movies = [["The Philosopher's Stone", 152, True],
          ["The Chamber of Secrets", 161, True],
          ["The Prisoner of Azkaban", 142, False],
          ["the Goblet of Fire", 157, True],
          ["the Order of the Phoenix", 138, False],
          ["the Half-Blood Prince", 153, True],
          ["the Deathly Hallows – Part 1", 126, False],
          ["the Deathly Hallows – Part 2", 130, False]]

À partir de cette liste, je souhaite avoir une liste ne comportant que les titres des films. C’est une transformation de chaque élément de la liste médias vers une liste de chaines de caractères. De manière classique, vous allez écrire le code suivant :

titles = []

for movie in movies:
    titles.append(movie[0])

Ça fait le job et vous avez une nouvelle liste de chaines de caractères.

Filtrer une liste

Filtrer une liste consiste, à partir d’une liste l, à obtenir une liste d’éléments de l correspondant à un critère. Reprenons notre liste medias, le dernier élément indique si j’ai vu le film. Je souhaite générer une liste des films que je n’ai pas encore vu, ceux pour lesquels le dernier élément est à False.

Le code classique est le suivant :

movies_to_see = []

for movie in movies:
    if not movie[-1]:
        movies_to_see.append(movie)

Ça fait aussi le job, vous avez une liste de listes qui correspond au critère.

Transformer et filtrer

On peut évidemment combiner les deux actions précédentes. Je souhaite obtenir une liste des titres des films qui me reste à voir. Le code est simplement :

titles_to_see = []

for movie in movies:
    if not movie[-1]:
        titles_to_see.append(movie)

Et là aussi, le job est fait, vous avez une liste de chaines de caractères correspondant au critère.

Tout ça c’est bien joli, mais c’est des algorithmes de C, pas de Python. Nous arrivons donc à nos compréhension lists.

Structure d’une compréhension list

Les comprehension lists vont nous permettre de simplifier l’écriture des codes précédents. Le premier usage d’une comprehension list est de permettre de simplifier la transformation d’une liste. Pour cela, la sémantique en pseudo-code est la suivante :

[transformation for element in iterable]

Il s’agit bien d’appliquer une transformation sur chaque élément d’un itérable. Ainsi, notre premier code devient :

titles = [movie[0] for movie in movies]

Et c’est tout. La magie n’est pas de faire tenir le code précédent en une seule ligne mais de le rendre lisible. De faire comprendre l’intention à la première lecture. Lorsque vous soyez cette structure, vous devez tout de suite comprendre qu’il s’agit d’une transformation de la liste.

Je vous ai parlé de transformation et de filtre. Et bien pour filtrer une liste avec cette structure, il suffit de rajouter la condition à la fin selon la structure suivante :

[transformation for element in iterable if condition]

Une transformation peut être une identité. Donc, le filtre des films qui me reste à voir s’écrira :

movies_to_see = [movie for movie in movies if not movie[-1]]

Et évidemment, transformer et filtrer s’écrira simplement avec le code suivant :

titles_to_see = [movie[0]
                 for movie in movies
                 if not movie[-1]]

Vous remarquerez dans ce dernier code que le but n’est pas de faire du one-liner mais bien de faciliter la lecture de l’intention du code. Si vous voyez une comprehension list, vous devez comprendre qu’il s’agit d’une transformation et/ou un filtre de la liste.

Il s’agit là des avantages des compréhension lists, mais on peut faire mieux avec un peu d’application.

Améliorer la lisibilité

Le code précédent peut encore être amélioré. Nous allons utiliser la propriété de l’unpacking qui permet en Python, lors d’une affectation, de déclarer autant de variables que d’élément de la collection à affecter. La conséquence est que chaque élément de la collection est affecté à la variable du même ordre. En conséquence, nous pouvons écrire la comprehension list de la manière suivante :

titles_to_see = [title
                 for title, _, seen in movies
                 if not seen]

Relisez bien cette ligne si nécessaire à haute voix. Vous comprenez maintenant l’intérêt de nommer correctement ses variables ?

En parlant de nommer ses variables, j’utilise ici une convention qui se développe peu à peu en nommant une variable _ soit en utilisant uniquement le caractère souligné. Ce nom ne veut rien dire ? C’est exacte, c’est le but. Cette convention part du principe qu’un code ne doit déclarer que des variables que si c’est utile. Or la durée ne nous sert à rien dans ce code. Mais la structure de l’unpacking nous oblige à déclarer une variable pour recevoir cette seconde donnée de la liste.

La convention est donc qu’ici, j’informe le lecteur que je reçois une donnée qui n’a aucun intérêt pour mon code. Il n’a pas à se demander quand et pourquoi je l’utilise, elle ne sert à rien.

Comprehension list dans un flux

Extraire des données d’éléments d’une liste pour en faire autre chose est une opération courante. D’ailleurs, à partir de la liste movies, nous pouvons calculer la durée totale (en minutes) de l’ensemble des films Harry Potter. Pour cela, nous pouvons utiliser la fonction sum(iterable) en lui fournissant une liste d’entier. L’instruction est alors simplement :

total_duration = sum([duration for _, duration, _ in movies])

Vous voyez que les comprehension lists permettent de simplifier le code Python afin d’en faciliter la lecture. En guise d’exercice, écrivez l’instruction qui permet de calculer la durée des épisodes qui me reste à voir.

Il y a une difficulté pour bien mettre en œuvre les comprehension lists, c’est de bien comprendre ce qu’est une transformation. Pour illustrer cela, je vous préparer un autre article pour bientôt.

Si vous avez aimé ce post, n’hésitez pas à laisser un commentaire ci-dessous ou sur la page Facebook 😉

Bonus : comprehension lists et génération de données

Une astuce des comprehension lists, c’est qu’elles génèrent des données à partir d’un itérable. C’est intéressant lorsque celui-ci est un compteur… Prenons le cas où vous voulez générer une liste des scores pour un nombre paramétrable de joueurs. Le code est simplement :

number_of_players = int(input('Nombre de joueurs ? '))

scores = [0 for _ in range(number_of_players)]

On peut aller plus loin avec une bibliothèque que je vous présente bientôt.

À propos de... Darko Stankovski

iT guy, photographe et papa 3.0, je vous fais partager mon expérience et découvertes dans ces domaines. Vous pouvez me suivre sur les liens ci-dessous.

Vous aimerez aussi...

Laisser un commentaire

En naviguant sur Dad 3.0, vous acceptez l’utilisation de cookies pour une navigation optimale et nous permettre de réaliser des statistiques de visites. Plus d'informations

Le blog Dad 3.0 utilise les cookies pour vous permettre une navigation optimale et nous permettre de réaliser des statistiques de visite. Dad 3.0 affichant des publicités, celles-si utilisent également des cookies pour un ciblage publicitaire. En continuant la navigation sur Dad 3.0, vous acceptez le dépôt et la lecture de cookies.

Fermer