UKOnline

Définition de liste

Le deuxième point d'attention pour un code « pythonique » concerne la définition de listes. La façon la plus simple de définir une liste consiste à partir d'une liste vide et d'y ajouter les éléments désirés à l'aide de la méthode append. Les éléments sont, par exemple, ajoutés l'un après l'autre grâce à une boucle, comme dans l'exemple suivant qui remplit la liste data avec les $n$ premières puissances de deux :

Deux techniques peuvent être utilisées pour améliorer le temps d'exécution de programmes créant des listes. Outre les performances, ces techniques améliorent également la lisibilité des programmes.

Fonctions map et filter

Lorsqu'il s'agit de construire une liste à partir d'une autre, en appliquant une opération sur chaque élément de cette dernière, on peut utiliser la fonction prédéfinie map. Celle-ci prend comme premier paramètre une fonction décrivant l'opération à appliquer à chaque élément de la liste passée en second paramètre. Lorsque la fonction à appliquer est simple, il est préférable de la définir à l'aide d'une fonction lambda, pour que le code soit le plus « pythonique » possible. Voici deux fonctions qui créent une liste avec les $n$ premières puissances de deux :

La seconde fonction est plus « pythonique » que la première et il faut privilégier ce style, même si au niveau des performances, elle n'est qu'un peu plus rapide que la première. En effet, on passe de 97 ms à 94 ms pour créer une liste avec les dix-mille premières puissances de $2$, soit une diminution de temps d'à peine 3 %.

On peut aussi vouloir construire une liste à partir d'une autre en ne gardant que certains éléments, ce que l'on peut faire avec la fonction prédéfinie filter. Celle-ci prend comme premier paramètre une fonction booléenne décrivant la condition que doivent satisfaire les éléments qu'il faut garder dans la liste passée en second paramètre. De nouveau, si la fonction est simple, mieux vaut utiliser une fonction lambda. Voici deux fonctions qui créent une liste avec les entiers compris entre $0$ et $n$ qui sont soit divisibles par deux, soit par trois :

Au niveau du style, mieux vaut utiliser la seconde fonction, même si les performances au niveau du temps d'exécution ne sont pas forcément meilleures. Pour cet exemple, la seconde fonction est même plus lente que la première. En effet, on passe de 193 ms à 228 ms pour créer une liste avec les entiers compris entre $0$ et un million qui sont divisibles par deux et trois, soit une augmentation de temps de 25 %.

En réalité, les fonctions map et filter n'améliorent pas forcément le temps d'exécution. On préfère néanmoins les utiliser pour deux autres raisons. Tout d'abord, la lisibilité du code est améliorée en évitant des niveaux d'indentation trop élevés dus à l'imbrication d'une instruction if dans une boucle for.

Ensuite, comme décrit dans la section suivante, ces deux fonctions améliorent l'occupation mémoire. Ce qui est en effet couteux au niveau du temps d'exécution, c'est l'instruction list(data) faite avant le return pour obtenir un objet de type list.

Liste en compréhension

La manière plus « pythonique » de créer une liste consiste à utiliser une définition en compréhension, inspirée de la définition mathématique des séquences. On préfère cette notation, plus lisible et concise, à la fonction map, car il ne faut pas définir de fonction. Voici comment la fonction précédente, qui calcule les dix-mille premières puissances de deux, se réécrit par compréhension :

Au niveau du temps d'exécution, cette nouvelle manière de définir la fonction prend plus ou moins le même temps que les deux versions précédentes. Son avantage est d'offrir un gain en lisibilité du code.

On peut également remplacer la fonction filter par une définition en compréhension en ajoutant une clause if. Voici comment la fonction précédente, qui crée une liste avec les entiers compris entre $0$ et un million qui sont divisibles par deux ou trois, se réécrit :

Concernant le temps d'exécution, cette nouvelle manière, de définir la fonction, est plus efficace. On passe en effet à 156 ms, c'est-à-dire une diminution de 19 % par rapport à la première version.

Cette façon, de définir une liste en compréhension, est plus qu'un simple sucre syntaxique. Outre produire un code plus « pythonique », plus concis, lisible et explicite, elle permet d'améliorer le temps d'exécution, par rapport à l'utilisation de append dans une boucle.

Concaténation de chaines de caractères

Comme mentionné dans la section précédente, la définition de liste en compréhension peut être utilisée pour améliorer les deux fonctions utilisant de la concaténation de chaines de caractères. Voici deux nouvelles versions des fonctions présentées précédemment :

Les temps d'exécution sont respectivement passés à 58 ms et 51 ms, soit des améliorations de 66 % et de 52 %, par rapport aux versions les plus lentes utilisant l'opérateur de concaténation.