Chapitre 11 – La compréhension de liste

La compréhension de liste est une expression qui permet de construire une liste à partir de tout autre type itérable (liste, tuple, chaîne de caractères…). Le résultat obtenu est toujours une liste.

1. Une compréhension de liste toute simple

Un exemple valant mieux qu’un long discours, nous allons créer une liste de nombres entiers :

liste_initiale = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

À l’aide d’une boucle for et de la méthode append(), nous allons ajouter à une nouvelle liste baptisée liste_2, chaque élément de liste_initiale multiplié par deux.

liste_initiale = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
liste_2 = []
for n in liste_initiale:
    liste_2.append(n*2)
print(liste_2)

Résultat: [0, 2, 4, 6, 8, 10 ,12, 14, 16, 18, 20, 22, 24, 26, 28, 30]

J’ai donc utilisé une boucle for pour écrire ce petit bout de code de cinq lignes. Mais j’aurais tout aussi bien pu utiliser une compréhension de liste pour obtenir le même résultat. Voici la syntaxe de cette dernière :

liste_initiale = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
liste_2 = [n*2 for n in liste_initiale]
print(liste_2)

Résultat: [0, 2, 4, 6, 8, 10 ,12, 14, 16, 18, 20, 22, 24, 26, 28, 30]

Que remarque-t-on? Le code est plus concis puisqu’il tient sur trois lignes au lieu de cinq. Vous allez me dire que la différence est minime et dans cet exemple tout simple, je dois reconnaître que vous avez raison! Mais attendez de voir la suite…

2. Une compréhension de liste avec une condition

Je souhaite  créer une nouvelle liste de nombres pairs multipliés par 2 à partir de liste_initiale qui contient, je vous le rappelle des nombres pairs et impairs. Il va donc falloir que j’introduise une condition pour ignorer les nombres impairs. Comparons une nouvelle fois le code écrit avec une boucle for et celui écrit avec une compréhension de liste.

Avec une boucle for
liste_initiale = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
liste_pair = []
for n in liste_initiale:
    if n % 2 == 0:
        liste_pair.append(n*2)
print(liste_pair)

Résultat: [0, 4, 8,  12, 16, 20, 24, 28]

Avec une compréhension de liste

J’introduis la condition à la fin de la compréhension de liste. Au lieu d’avoir six lignes de code, je n’en ai plus que trois.

liste_initiale = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
liste_pair = [n*2 for n in liste_initiale if n % 2 == 0]
print(liste_pair)

Résultat: [0, 4, 8,  12, 16, 20, 24, 28]

3. Une compréhension de liste avec une expression conditionnelle

Encore plus fort, je souhaite créer une nouvelle liste avec les éléments de liste_initiale. Les chiffres pairs seront multipliés par deux, les chiffres impairs par trois. Il va donc falloir que j’introduise une condition if… else.

Avec une boucle for
liste_initiale = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
nouvelle_liste = []
for n in liste_initiale:
    if n % 2 == 0:
        nouvelle_liste.append(n*2)
    else:
        nouvelle_liste.append(n*3)
print(nouvelle_liste)

Résultat : [0, 3, 4, 9, 8, 15, 12, 21, 16, 27, 20, 33, 24, 39, 28, 45]

Avec une compréhension de liste

Ça se complique un peu car il faut utiliser une expression conditionnelle qui évalue si la condition est vraie (True) ou fausse (False). Mais le résultat est au rendez-vous puisqu’au lieu d’avoir huit lignes de code, je n’en ai que trois!

liste_initiale = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
nouvelle_liste = [n*2 if n % 2 == 0 else n*3 for n in liste_initiale]
print(nouvelle_liste)
 4. Une compréhension de liste avec une liste de listes

Il est possible de faire une compréhension de liste avec une liste de listes. Pour s’y retrouver dans l’imbrication des instructions, le plus simple est de comparer la syntaxe d’une boucle for avec celle d’une compréhension de liste.

Avec une boucle for
liste_initiale = [[0, 'a'], [2, 'b'], [3, 'c']]
nouvelle_liste = []
for sous_liste in liste_initiale :
    for n in sous_liste :
        nouvelle_liste.append(n*2)
print(nouvelle_liste)

Résultat: [0, ‘aa’, 4, ‘bb’, 6, ‘cc’]

Avec une compréhension de liste
liste_initiale = [[0, 'a'], [2, 'b'], [3, 'c']]
nouvelle_liste = [n*2 for sous_liste in liste_initiale for n in sous_liste]
print(nouvelle_liste)

Résultat: [0, ‘aa’, 4, ‘bb’, 6, ‘cc’]

Avec une expression conditionnelle

Par exemple, je veux multiplier les nombres entiers par deux et les strings par trois.

liste_initiale = [[0, 'a'], [2, 'b'], [3, 'c']]
nouvelle_liste = [n*2 if type(n) == int else n*3 for sous_liste in liste_initiale for n in sous_liste]
print(nouvelle_liste)

Résultat: [0, ‘aaa’, 4, ‘bbb’, 6, ‘ccc’]

Dans ce dernier exemple, si j’avais fait usage d’une boucle for, j’aurais écrit neuf lignes de code au lieu de trois!

liste_initiale = [[0, 'a'], [2, 'b'], [3, 'c']]
nouvelle_liste = []
for sous_liste in liste_initiale:
    for n in sous_liste:
        if type(n) == int:
            nouvelle_liste.append(n*2)
        else:
            nouvelle_liste.append(n*3)
print(nouvelle_liste)

Cela dit, ce n’est pas non plus le concours de celui qui écrira le plus d’instructions sur la même ligne de code. Il faut que ça reste déchiffrable.

5. Les compréhensions de liste avec les autres types itérables

Comme je le disais en introduction, les compréhensions de liste fonctionnent avec les autres types itérables mais le résultat obtenu est toujours une liste. Par exemple, avec une chaîne de caractères :

prenom = "Gustave"
liste_lettres = [lettre for lettre in prenom]
print(liste_lettres)

Résultat: [‘G’, ‘u’, ‘s’, ‘t’, ‘a’, ‘v’, ‘e’]

Conclusion

La compréhension de liste est une méthode puissante qui a remplacé les anciennes fonctions map() et filter() dont l’usage est aujourd’hui déconseillé. Bien utilisée, la compréhension de liste rend le code plus concis, plus élégant et plus facile à comprendre. Voici la page consacrée aux compréhensions de liste dans la documentation officielle du langage Python.

Il existe également une compréhension de dictionnaire et d’ensemble que nous aborderons dans le chapitre consacré à ces deux structures de données.

Chapitre 10 – Les références partagées et le module copy

D’aucuns pensent que la manière la plus simple de copier une liste est de déclarer une variable et de lui affecter ladite liste comme dans l’exemple ci-dessous:

liste_initiale = [1, 6.3, ['Alphonse', 'Solange']]
copie_liste = liste_initiale
print(copie_liste)

Résultat : [1, 6.3, [‘Alphonse’, ‘Solange’]]

Mais en faisant cela, nous avons simplement créé un alias, c’est-à-dire que nous avons instancié un nouvel objet qui partage la même référence que la liste copiée! Pour nous en convaincre, visualisons le code avec Python Tutor.

capture_1
Lire la suite de « Chapitre 10 – Les références partagées et le module copy »

Chapitre 9 – Les listes

Une liste est un ensemble d’éléments séparés par des virgules et entourés de crochets. Ces éléments peuvent être de n’importe quel type: str(), int(), float(). Il existe même des listes de listes! Contrairement aux chaînes de caractères, il est possible de modifier une liste grâce au slicing et à différentes méthodes que je vais vous présenter dans ce chapitre.

pythontutor

Déclarer une liste

Pour déclarer une liste vide, il existe deux syntaxes différentes:

liste_1 = []
liste_2 = list()
print(liste_1, liste_2)

Résultat : [] []

Dans le premier cas, je crée une liste vide. Dans le deuxième cas, je crée un objet de la classe list(). Mais vous pouvez tout à fait déclarer une liste et lui affecter directement des valeurs :

liste_3 = [8, 6.9, "Bertrance"]
print(liste_3)

Résultat : [8,  6.9, ‘Bertrance’] 

La deuxième syntaxe, list(), est utilisée pour convertir par exemple un tuple en liste. Nous verrons les tuples dans un prochain chapitre mais sachez déjà qu’un tuple est un ensemble d’éléments immuable séparés par des virgules et entourés de parenthèses. En fait, un tuple est une liste qu’on ne peut pas modifier. La seule solution est donc de transformer le tuple en liste.

tuple_prenoms = ("Jobriel", "Delphonse", "Bertrance")
liste_prenoms = list(tuple_prenoms)
print(liste_prenoms)

Résultat: [‘Jobriel’, ‘Delphonse’, ‘Bertrance’]

La méthode append( )

Grâce à la méthode append(), vous pouvez ajouter des éléments à une liste. Ils seront indexés à la fin de celle-ci.

prenoms = ["Jobriel", "Delphonse", "Bertrance"]
prenoms.append("Gustule")
print(prenoms)

Résultat : [‘Jobriel’, ‘Delphonse’, ‘Bertrance’, ‘Gustule’]

La fonction intégrée len( )

La fonction intégrée len() retourne un entier qui correspond au nombre d’éléments contenus dans la liste passée en argument.

print(len(prenoms))

Résultat : 4

Accéder à un élément d’une liste grâce à son indice

La variable prenoms contient quatre éléments indicés à partir de zéro, c’est-à-dire que l’indice du quatrième élément est 3. Avec l’indice, il est possible d’accéder à un élément précis de la liste. La syntaxe est la suivante. Je place l’indice de l’élément recherché, entre crochets.

personne = prenoms[2] 
print(personne)

Résultat : ‘Bertrance’.

Cela correspond au troisième élément de la liste, c’est-à-dire celui qui porte l’indice 2.

La méthode insert( )

La méthode insert(indice, nouvel_element) permet d’intercaler un nouvel élément dans une liste:

prenoms.insert(1, "Amandale") 
print(prenoms)

Résultat : [‘Jobriel’, ‘Amandale’, ‘Delphonse’, ‘Bertrance’, ‘Gustule’]

Cette méthode intercale l’élément passé en argument à l’indice passé en argument. « Amandale » se retrouve donc à l’indice 1 et les valeurs qui suivent, changent d’indice (i devient i + 1). Ainsi, « Delphonse » qui était à l’indice 1, se retrouve à l’indice 2.

La méthode extend( )

La méthode extend() permet de concaténer deux listes :

jours = ["Lundi", "mardi", "mercredi"]
jours2 = ["Jeudi", "Vendredi"]
jours.extend(jours2)
print(jours)

Résultat : [« Lundi », « Mardi », « Mercredi », « Jeudi », « Vendredi »]

Attention! Il ne faut pas confondre extend() et append()! La méthode extend() effectue une concaténation de listes tandis qu’append() rajoute l’élément passé en argument, à la fin de la liste sur laquelle la méthode est appliquée. Dans l’exemple précédent, voici ce que j’aurais obtenu si j’avais utilisé la méthode append():

jours = ["Lundi", "mardi", "mercredi"]
jours2 = ["Jeudi", "Vendredi"]
jours.append(jours2)
print(jours)

Résultat :  [« Lundi », « mardi », « mercredi », [« Jeudi », « Vendredi »]]

Je n’ai pas concaténé deux  listes mais j’ai rajouté la liste jours2 aux éléments de la liste jours. Cette dernière ne contient donc pas 5 éléments (comme dans le premier exemple) mais quatre éléments : Trois chaînes de caractères et une liste! Pour vous en assurer, il vous suffit d’utiliser la fonction intégrée len().

Accéder à un élément d’une liste qui se trouve elle-même dans une autre liste

jours est donc une liste qui contient une autre liste plus petite. Nous avons vu qu’il était possible d’accéder à un élément en utilisant l’indice entouré de crochets. Mais comment faire pour accéder à « Vendredi » qui est un élément de la petite liste qui se trouve en dernière position de la liste principale? C’est très simple! Il suffit d’utiliser cette syntaxe :

jours = ["Lundi", "mardi", "mercredi", ["Jeudi", "Vendredi"]]
element = jours[3][1]
print(element)

Résultat: Vendredi. Cette valeur correspond donc à jours[3][1].

  • 3 est l’indice de la petite liste dans la liste principale.
  • 1 est l’indice de « Vendredi » dans la petite liste.

Utiliser le slicing pour modifier une liste

Il est tout à fait possible d’utiliser les crochets et les indices pour insérer ou supprimer des éléments dans une liste. Cette technique s’appelle le slicing. Cela se traduit par découpe en tranches. C’est clairement plus délicat à manipuler que les méthodes mais c’est très efficace et d’une grande souplesse. Par exemple, au lieu d’appliquer la méthode list.insert(), on peut utiliser la syntaxe list[i:i].

ours = ["Lundi", "Mardi", "Jeudi"]
jours[2:2] = ["Mercredi"]
print(jours)

Résultat: [‘Lundi’, ‘Mardi’, ‘Mercredi’, ‘Jeudi’]

Attention! L’élément à insérer doit forcément être une liste. Vous pouvez insérer plusieurs éléments mais si vous n’en insérez qu’un seul, comme dans l’exemple ci-dessus, il faut le présenter entre crochets et par conséquent le convertir en une liste d’un seul élément.

Si vous oubliez les crochets, Python va insérer chaque caractère de la chaîne « Mercredi » en tant qu’élément. Ce n’est sans doute pas le but recherché!

[‘Lundi’, ‘Mardi’, ‘M’, ‘e’, ‘r’, ‘c’, ‘r’, ‘e’, ‘d’, ‘i’, ‘Jeudi’]

Pour faire un print() d’une partie des éléments d’une liste, par exemple les éléments indicés 1 et 2 dans notre liste prenoms, le code sera celui-ci :

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
print(prenoms[1:3])

Résultat: [‘Amandale’, ‘Delphonse’]

Dans cette tranche, l’élément indicé 1 est pris en compte tout comme l’élément indicé 2 mais pas l’élément indicé 3! Voici un schéma qui permet de mieux comprendre comment fonctionne le slicing.

['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
|          |           |            |            |         |
0          1           2            3            4         5 

La tranche [1:3] correspond à « Amandale » et « Delphonse ». La tranche [3:4] correspond au prénom « Bertrance ». Quant au dernier chiffre (5), il correspond au len(), c’est-à-dire au nombre d’éléments que contient la liste.

Si j’insère « Hervine » dans la tranche [1:3], alors ce prénom remplace « Amandale » et « Delphonse ».

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
prenoms[1:3] = ["Hervine"]  # Ne pas oublier les crochets!
print(prenoms)

Résultat : [‘Jobriel’, ‘Hervine’, ‘Bertrance’, ‘Gustule’]

Autres exemples de slicing

prenoms[:][‘Jobriel’, ‘Amandale’, ‘Delphonse’, ‘Bertrance’, ‘Gustule’]
prenoms[1:][‘Amandale’, ‘Delphonse’, ‘Bertrance’, ‘Gustule’]
prenoms[:-1][‘Jobriel’, ‘Amandale’, ‘Delphonse’, ‘Bertrance’]
prenoms[:-2][‘Jobriel’, ‘Amandale’, ‘Delphonse’]
prenoms[-1]‘Gustule’ (équivaut à prenoms[4])
prenoms[::2][‘Jobriel’, ‘Delphonse’, ‘Gustule’] (un élément sur deux)
prenoms[::3][‘Jobriel’, ‘Bertrance’] (un élément sur trois)
prenoms[::-1][‘Gustule’, ‘Bertrance’, ‘Delphonse’, ‘Amandale’, ‘Jobriel’]
inverse les éléments

Il n’est pas toujours évident de manipuler les indices négatifs. Pour vous faciliter la tâche, il faut savoir que prenoms[-3] correspond à prenoms[len(prenoms) – 3] c’est-à-dire à prenoms[1].

[-1] est un indice négatif particulièrement intéressant car il permet d’accéder au dernier  élément d’une liste sans connaître la longueur de celle-ci.

Il est possible de parcourir une liste en utilisant un pas qui ne prend qu’un élément sur deux [::2] ou qu’un élément sur trois [::3] par exemple. Il existe une technique de slicing très intéressante: Un pas de [::-1] inverse les éléments d’une liste.

méthodes associées aux objets de type list()

Nous avons déjà vu list.append(), list.extend() et list.insert(). Il existe également :

  • list.sort()

Effectue le tri d’une liste dans l’ordre numérique croissant (ou dans l’ordre alphabétique croissant).

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
prenoms.sort()
print(prenoms)

Résultat: [‘Amandale’, ‘Bertrance’, ‘Delphonse’, ‘Gustule’, ‘Jobriel’]

list.sort() peut prendre l’argument reverse = True. Dans ce cas, le tri est décroissant :

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
prenoms.sort(reverse = True)
print(prenoms)

Résultat : [‘Jobriel’, ‘Gustule’, ‘Delphonse’, ‘Bertrance’, ‘Amandale’]

  • list.index()

list.index() retourne l’index de l’élément passé en argument. Si l’élément est absent de la liste, Python lève une exception. Si l’élément est présent plusieurs fois, la méthode retourne l’index de la première occurrence.

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
i = prenoms.index("Amandale")
print(i)

Résultat : 1

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
j = prenoms.index("Flatule")
print(j)

ValueError: ‘Flatule’ is not in list

Si une valeur est présente plusieurs fois, il est possible de passer en argument l’index à laquelle doit débuter la recherche sous la forme list.index(valeur, i):

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Jobriel', 'Gustule']
i = prenoms.index("Jobriel", 2)
print(i)

Voir les articles

Résultat: 3  (la première valeur ‘Jobriel’ est ignorée)

  • list.reverse()

Cette méthode inverse les valeurs de la liste. Elle est équivalente à la technique de slicing [::-1]

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
prenoms.reverse()
print(prenoms)

Résultat: [‘Gustule’, ‘Bertrance’, ‘Delphonse’, ‘Amandale’, ‘Jobriel’]

  • list.pop()

list.pop() est une drôle de méthode. Elle supprime et retourne par défaut, la dernière valeur de la liste. Cette méthode peut prendre un indice en argument. Dans ce cas, c’est la valeur indicée qui est supprimée et retournée. Amusons-nous à poper deux exemples!

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
popped = prenoms.pop()
print(popped, prenoms)

Résultat: ‘Gustule’ [‘Jobriel’, ‘Amandale’, ‘Delphonse’, ‘Bertrance’]

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
popped = prenoms.pop(2)
print(popped, prenoms)

Résultat: ‘Delphonse’ [‘Jobriel’, ‘Amandale’, ‘Bertrance’, ‘Gustule’]

  • list.remove()

Cette méthode supprime la première occurrence de la valeur passée en argument. Voici un exemple avec une liste contenant deux « Jobriel ».

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Jobriel', 'Gustule']
prenoms.remove("Jobriel")
print(prenoms)

Résultat: [‘Amandale’, ‘Delphonse’, ‘Jobriel’, ‘Gustule’]

  • list.count()

Cette méthode compte le nombre de fois où une occurrence apparaît dans une liste et elle retourne la valeur correspondante.

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Jobriel', 'Gustule']
numb = prenoms.count("Jobriel")
print(numb)

Résultat: 2

Tester l’appartenance d’une valeur à une liste

L’instruction conditionnelle if… in autorise très facilement ce genre de test. Elle valide la présence ou l’absence de la valeur recherchée dans la liste.

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
if "Bertrance" in prenoms:
    print("Le test d'appartenance est positif.") 
else:
    print("Le test d'appartenance est négatif.")

Résultat : « Le test d’appartenance est positif. »

prenoms = ['Jobriel', 'Amandale', 'Delphonse', 'Bertrance', 'Gustule']
result = "Maxouelle" in prenoms
print(result)

Résultat : False

Chapitre 8 – Quelques méthodes sur les objets de type string( )

En Python, tout est objet. Les chaînes de caractères sont par exemple des objets créés par instanciation de la classe str(). Une classe, c’est un peu comme une caisse à outils qui permet de créer un objet et de le façonner. La classe contient des méthodes et des variables que l’on pourrait comparer aux outils d’un menuisier:

Lire la suite de « Chapitre 8 – Quelques méthodes sur les objets de type string( ) »

Chapitre 6 – Les fonctions

Une fonction est une portion de code qui effectue une tâche ou un calcul relativement indépendant du reste du programme. Une fonction comporte des paramètres d’entrée sur lesquels elle travaille avant de retourner une valeur de sortie qui peut être stockée dans une variable. Si la fonction ne retourne rien (None), on parlera plutôt de procédure. Une fonction est un sous-programme réutilisable partout dans le code. Il est donc inutile de réécrire des instructions identiques. Il suffit de les inclure dans une fonction et d’appeler cette dernière à chaque fois que cela est nécessaire.

Lire la suite de « Chapitre 6 – Les fonctions »

Chapitre 5 – Les booléens

Aujourd’hui, je vais vous parler des booléens. Les booléens sont des êtres farouches qui vivent au fin fond des forêts binaires recouvrant la terre sacrée du Grand Python Digital. Très difficiles à observer dans leur milieu naturel, bla, bla, bla… Bon en fait, il s’agit d’une notion de programmation un peu déroutante que j’avais du mal à conceptualiser au début de mon apprentissage du Python.

Lire la suite de « Chapitre 5 – Les booléens »

Chapitre 3 – Les conditions

Dans un programme écrit en langage Python, les instructions s’exécutent les unes après les autres. Par exemple, dans ce code, je commence par déclarer une variable a à laquelle j’affecte le nombre entier 5. Je fais un print qui m’affiche 5 puis à la ligne suivante, je modifie ma variable a avant de refaire un print. Enfin, je déclare une variable b à laquelle j’affecte le nombre entier 10 et je fais un print qui m’affiche l’addition de a + b. Chaque instruction s’exécute l’une après l’autre.

Lire la suite de « Chapitre 3 – Les conditions »

Chapitre 2 – Déclarer une variable

1. Définition d’une variable

Le travail d’un ordinateur, c’est de manipuler des données qui sont stockées dans sa mémoire. Or, pour cela, il faut un minimum d’organisation. C’est la raison pour laquelle les ordinateurs utilisent des variables, lesquelles correspondent à des adresses mémoire bien précises. Une variable, c’est donc avant tout une adresse mémoire mais c’est également un endroit que l’on réserve dans la mémoire de l’ordinateur. Lorsqu’on déclare une variable, on créé un petit tiroir sur lequel on colle une étiquette (qui correspond au nom de la variable) et dans lequel on stocke une valeur.

Lire la suite de « Chapitre 2 – Déclarer une variable »