Les listes peuvent être formées :
-
en évaluant l’expression
list()ou[]. On obtient la liste vide. - En combinant des éléments
[elt0, elt1, …, eltn], ou en convertissant une séquence parlist(seq). -
en “compréhension”, par
[exprforindiceiniterable], ou[exprforindiceiniterableifcondition]. La liste est ici formée des valeurs de expr quand indice parcourt iterable (et oùconditionest facultative). Un “itérable” est tout objet qui peut être traversé, parcouru : (les séquences, les ensembles, les dictionnaires…)
L’expression suivante forme par exemple la liste des {x^2} où 1 ≤ x < 100, en se limitant à x ≡ 3 modulo 10.
12>>> [x*x for x in range(1,100) if x%10 == 3][9, 169, 529, 1089, 1849, 2809, 3969, 5329, 6889, 8649]
En fait, les listes peuvent être construites “en compréhension” d’une façon plus générale encore.
La syntaxe est alors
|
1 2 3 4 |
[ expression for indice_1 in iterable_1 [if condition_1] for indice_2 in iterable_2 [if condition_2] ... for indice_n in iterable_n [if condition_n] ] |
Chacune des conditions est facultative (et la condition n˚i s’applique à l’indice n˚i).
Dans cette syntaxe, expression est évaluée en fonction des valeurs des {n}-uplets {(\mathrm{indice}_1,\mathrm{indice}_2,\ldots,\mathrm{indice}_n)} successifs (dans l’ordre lexicographique) et le résultat est la liste de ces évaluations de expression.
Il faut bien comprendre la chronologie : pour chacune des valeurs possibles de indice1 (définies par iterable1 et le test éventuel condition1), on fait varier indice2 (attention : iterable2 et condition2 peuvent dépendre de indice1), puis (à indice1 et indice2 fixés) on fait varier indice3, etc.
Ainsi la boucle sur indice1 “contient” la boucle sur indice2, qui contient elle-même, etc., jusqu’à la boucle sur indicen.
Voici deux premiers exemples où une liste est formée par compréhension avec deux for imbriqués. On voit que cela produit deux variables locales i et j, et que la première boucle “contient” la deuxième. Cette chronologie est importante, comme on le voit sur le troisième exemple, qui conduit à une erreur.
|
1 2 3 4 5 6 7 |
>>> [100*i+j for i in range(1,5) for j in range(1,4)] [101, 102, 103, 201, 202, 203, 301, 302, 303, 401, 402, 403] >>> [100*i+j for i in range(1,5) for j in range(1,i+1)] [101, 201, 202, 301, 302, 303, 401, 402, 403, 404] >>> [100*i+j for j in range(1,i+1) for i in range(1,5)] <...> NameError: name 'i' is not defined |
Voici quatre autres exemples, qui doivent être soigneusement comparés avec les précédents. Ici la liste calculée s’écrit [expression for indice in intervalle], où expression est elle-même définie comme une liste en compréhension. La boucle permettant de calculer expression est parcourue “sous le contrôle” de la boucle principale.
|
1 2 3 4 5 6 7 8 9 |
>>> [[100*i+j for i in range(1,5)] for j in range(1,4)] [[101, 201, 301, 401], [102, 202, 302, 402], [103, 203, 303, 403]] >>> [[100*i+j for j in range(1,4)] for i in range(1,5)] [[101, 102, 103], [201, 202, 203], [301, 302, 303], [401, 402, 403]] >>> [[100*i+j for j in range(1,i+1)] for i in range(1,5)] [[101], [201, 202], [301, 302, 303], [401, 402, 403, 404]] >>> [[100*i+j for i in range(1,5)] for j in range(1,i+1)] <...> NameError: name 'i' is not defined |
L’utilisation de listes en compréhension permet des constructions élégantes. L’imbrication des définitions en compréhension peut cependant conduire à des formulations assez délicates à relire, comme l’atteste cette fonction qui renvoie la liste des facteurs premiers strictement inférieurs à {n^2}, où n est l’entier passé en argument :
|
1 2 3 4 |
def cribleobscur(n): return [p for p in range(2,n*n) if p not in [j for i in range(2,n) for j in range(2*i,n*n,i)]] >>> cribleobscur(10) [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97] |