Aller au contenu

Chapitre 2.1 - Les Listes⚓︎

image

Les listes font partie de ce qu'on appelle les données composites (nous verrons plus tard les tuples et les dictionnaires). Elles permettent de regrouper de manière structurée un ensemble de valeurs (et non plus une valeur unique). On les appelle listes en Python, ou bien tableaux de manière plus générale.

1. Déclaration d'une liste⚓︎

Exemple fondateur n°1 ❤

Une variable de type liste sera délimitée par des crochets, et ses éléments séparés par des virgules :

Python
>>> maliste = ["riri", "fifi", "loulou"]

On peut observer le type de la variable ainsi créée :

Python
>>> type(maliste)
<class 'list'>

Remarques :

  • Même si cela n'a ici un grand intérêt, les éléments d'une liste peuvent donc être de types différents : ici, tous les éléments de ma liste sont des chaînes de caractères (str), mais la liste ["riri", 5, "fifi", "loulou"] est aussi une liste valide.

  • Une liste vide se déclarera avec [].

    Python
    >>> copies_corrigees = []
    
    Nous verrons plus tard qu'il est fréquent dans les exercices de partir d'une liste vide et d'ajouter progressivement des éléments.

2. Accès aux éléments d'une liste⚓︎

Exemple fondateur n°2 ❤

On accède à un élément d'une liste en mettant entre crochets l'indice de l'élément (qui commence à zéro).

Python
>>> famille = ["Bart", "Lisa", "Maggie"] # (1)
>>> famille[0]
'Bart'
>>> famille[1]
'Lisa'
>>> famille[2]
'Maggie'
>>> famille[3]
Traceback (most recent call last):
  File "<pyshell>", line 1, in <module>
IndexError: list index out of range

  1. Nous prendrons souvent l'exemple de la famille Simpsons.

Remarques :

  • Un indice qui dépasse la valeur  longueur de la liste -1 provoquera donc une erreur list index out of range. C'est une erreur très fréquente lorsqu'on manipule des listes. image

  • Il est par contre possible d'utiliser des indices négatifs. On utilise par exemple très souvent l'indice -1 pour accéder au dernier élément de la liste, sans avoir à connaître la longueur de celle-ci :

Python
>>> famille[-1]
'Maggie'

3. Longueur d'une liste⚓︎

Exemple fondateur n°3 ❤

La longueur d'une liste sera donnée par la fonction len(), qui renvoie donc un nombre entier positif ou nul.

Python
>>> len(famille)
3

Remarques :

  • La liste vide a pour longueur 0 :
    Python
    >>> len([])
    0
    
  • Le dernier élément d'une liste maliste (non vide) sera donc toujours l'élément d'indice len(maliste)-1.
    Python
    >>> famille[len(famille) - 1]
    'Maggie'
    
  • Comme indiqué précédemment, ce dernier élément est aussi accessible par l'indice -1.

4. Parcours des éléments d'une liste ⭐ ⭐ ⭐⚓︎

Il existe deux méthodes pour parcourir séquentiellement tous les éléments d'une liste. Ces deux méthodes sont à maîtriser impérativement.

4.1 Parcours «par éléments»⚓︎

C'est la méthode la plus naturelle, celle déjà vue lors de la présentation de la boucle for. Nous allons simplement itérer sur les éléments de la liste.

Exemple fondateur n°4 ❤

Le code :

Python
1
2
3
4
famille = ["Bart", "Lisa", "Maggie"]

for membre in famille:
    print(membre)
renverra :
Python
Bart
Lisa
Maggie

Remarque :

  • Penser à donner un nom signifiant à la variable qui parcourt la liste. Il aurait par exemple été très maladroit d'écrire
    Python
    for k in famille:
        print(k)
    
    En effet le nom de variable k est habituellement utilisé pour les nombres (les indices, les compteurs...).
Exercice 1

Après un référendum, la liste urne contient uniquement des 'oui' ou des 'non'. Déterminer le vainqueur de ce référendum.

Python
urne = ['oui', 'non', 'non', 'oui', 'oui', 'oui', 'non', 'oui', 'oui', 'oui', 'non', 'oui', 'non', 'non', 'oui', 'non', 'non', 'oui', 'oui', 'oui', 'oui', 'non', 'non', 'oui', 'non', 'oui', 'non', 'non', 'non', 'oui', 'oui', 'oui', 'oui', 'non', 'oui', 'non', 'non', 'oui', 'non', 'non', 'oui', 'non', 'non', 'non', 'non', 'non', 'non', 'non', 'oui', 'non', 'non', 'oui', 'oui', 'non', 'oui', 'oui', 'oui', 'oui', 'oui', 'oui', 'non', 'non', 'oui', 'oui', 'oui', 'oui', 'non', 'non', 'non', 'oui', 'oui', 'oui', 'non', 'non', 'non', 'non', 'oui', 'non', 'non', 'non', 'non', 'non', 'non', 'non', 'non', 'oui', 'non', 'oui', 'non', 'non', 'oui', 'oui', 'non', 'non', 'non', 'oui', 'oui', 'non', 'oui', 'oui', 'non', 'non', 'oui', 'oui', 'oui', 'non', 'oui', 'non', 'oui', 'oui', 'oui', 'oui', 'oui', 'non', 'non', 'non', 'non', 'oui', 'oui', 'oui', 'non', 'oui', 'non', 'oui', 'oui', 'oui', 'oui', 'oui', 'non', 'non', 'oui', 'non', 'non', 'non', 'non', 'non', 'non', 'oui', 'non', 'non', 'oui', 'non', 'non', 'oui', 'non', 'non', 'oui', 'oui', 'oui', 'oui', 'oui', 'oui', 'non', 'oui']
Python
urne = ['oui', 'non', 'non', 'oui', 'oui', 'oui', 'non', 'oui', 'oui', 'oui', 'non', 'oui', 'non', 'non', 'oui', 'non', 'non', 'oui', 'oui', 'oui', 'oui', 'non', 'non', 'oui', 'non', 'oui', 'non', 'non', 'non', 'oui', 'oui', 'oui', 'oui', 'non', 'oui', 'non', 'non', 'oui', 'non', 'non', 'oui', 'non', 'non', 'non', 'non', 'non', 'non', 'non', 'oui', 'non', 'non', 'oui', 'oui', 'non', 'oui', 'oui', 'oui', 'oui', 'oui', 'oui', 'non', 'non', 'oui', 'oui', 'oui', 'oui', 'non', 'non', 'non', 'oui', 'oui', 'oui', 'non', 'non', 'non', 'non', 'oui', 'non', 'non', 'non', 'non', 'non', 'non', 'non', 'non', 'oui', 'non', 'oui', 'non', 'non', 'oui', 'oui', 'non', 'non', 'non', 'oui', 'oui', 'non', 'oui', 'oui', 'non', 'non', 'oui', 'oui', 'oui', 'non', 'oui', 'non', 'oui', 'oui', 'oui', 'oui', 'oui', 'non', 'non', 'non', 'non', 'oui', 'oui', 'oui', 'non', 'oui', 'non', 'oui', 'oui', 'oui', 'oui', 'oui', 'non', 'non', 'oui', 'non', 'non', 'non', 'non', 'non', 'non', 'oui', 'non', 'non', 'oui', 'non', 'non', 'oui', 'non', 'non', 'oui', 'oui', 'oui', 'oui', 'oui', 'oui', 'non', 'oui']

nb_oui = 0
nb_non = 0
for vote in urne:
    if vote == 'oui':
        nb_oui += 1
    else:
        nb_non += 1
print('pourcentage de oui', 100*nb_oui/len(urne), '%')
print('pourcentage de non', 100*nb_non/len(urne), '%')
print('-'*10)
if nb_oui > nb_non:
    print('le oui est vainqueur')
elif nb_oui < nb_non:
    print('le non est vainqueur')
else:
    print('le oui et le non sont à égalité')

4.2 Parcours «par indice»⚓︎

Chaque élément étant accessible par son indice (de 0 à len(liste) - 1 ), il suffit de faire parcourir à une variable i l'ensemble des entiers de 0 à len(liste) - 1, par l'instruction range(len(liste)) :

Exemple fondateur n°5 ❤

Le code :

Python
1
2
3
4
famille = ["Bart", "Lisa", "Maggie"]

for i in range(len(famille)):
    print(famille[i])
renverra :
Python
Bart
Lisa
Maggie

⚠ Bonne habitude à prendre : nommer sa variable d'indice i, j, k ou indice mais pas autre chose !

image

4.3 Avantages et inconvénients de chaque méthode⚓︎

4.3.1 Parcours par élément⚓︎

Python
    for membre in famille:
        print(membre)

Les avantages 👍

  • la simplicité : un code plus facile à écrire, avec un nom de variable explicite.
  • la sécurité : pas de risque d'index out of range !

Les inconvénients 👎

  • méthode rudimentaire : lorsqu'on est «positionné» sur un élément, il n'est pas possible d'accéder au précédent ou au suivant. (et c'est parfois utile).
  • on ne peut pas modifier l'élément sur lequel on est positionné :
Python
>>> lst = [1, 2, 3]
>>> for nb in lst:
        nb = nb * 2 # (1)

>>> lst 
[1, 2, 3] # (2)
  1. On veut multiplier par 2 chaque élément de la liste
  2. La liste lst n'a pas changé...

4.3.2 Parcours par indice⚓︎

Python
    for i in range(len(famille)):
        print(famille[i])

Les avantages 👍

  • le contrôle : en parcourant par indice, on peut s'arrêter où on veut, on peut accéder au suivant/précédent...
  • pour les tableaux à deux dimensions, on retrouve la désignation classique d'un élément par numéro de ligne / numéro de colonne.

Les inconvénients 👎

  • la complexité : il faut connaître le nombre d'éléments de la liste (ou le récupérer par la fonction len() )
  • le risque d'erreur : encore et toujours le index out of range...
Exercice 2

On donne la liste :

Python
lst = [3, 1, 4, 1, 5, 9]
En utilisant un parcours par indice :

  1. Afficher un à un les éléments de cette liste.
  2. Afficher un à un les éléments de cette liste dans l'ordre inverse (en commençant par 9)
  1. Python
    for i in range(len(lst)):
        print(lst[i])
    

2.

Python
1
2
3
4
lst = [3, 1, 4, 1, 5, 9]

for i in range(len(lst)-1, -1, -1):
    print(lst[i])
}

Exercice 3

Trouvez le nombre qui est exactement à la même place dans la liste list1 et dans la liste list2, sachant que les deux listes ont la même taille.

  1. En utilisant une boucle for (une seule !).
  2. En utilisant une boucle while. Quel est l'avantage de la boucle while ?
Python
list1 = [8468, 4560, 3941, 3328, 7, 9910, 9208, 8400, 6502, 1076, 5921, 6720, 948, 9561, 7391, 7745, 9007, 9707, 4370, 9636, 5265, 2638, 8919, 7814, 5142, 1060, 6971, 4065, 4629, 4490, 2480, 9180, 5623, 6600, 1764, 9846, 7605, 8271, 4681, 2818, 832, 5280, 3170, 8965, 4332, 3198, 9454, 2025, 2373, 4067]
list2 = [9093, 2559, 9664, 8075, 4525, 5847, 67, 8932, 5049, 5241, 5886, 1393, 9413, 8872, 2560, 4636, 9004, 7586, 1461, 350, 2627, 2187, 7778, 8933, 351, 7097, 356, 4110, 1393, 4864, 1088, 3904, 5623, 8040, 7273, 1114, 4394, 4108, 7123, 8001, 5715, 7215, 7460, 5829, 9513, 1256, 4052, 1585, 1608, 3941]
Python
1
2
3
4
5
6
list1 = [8468, 4560, 3941, 3328, 7, 9910, 9208, 8400, 6502, 1076, 5921, 6720, 948, 9561, 7391, 7745, 9007, 9707, 4370, 9636, 5265, 2638, 8919, 7814, 5142, 1060, 6971, 4065, 4629, 4490, 2480, 9180, 5623, 6600, 1764, 9846, 7605, 8271, 4681, 2818, 832, 5280, 3170, 8965, 4332, 3198, 9454, 2025, 2373, 4067]
list2 = [9093, 2559, 9664, 8075, 4525, 5847, 67, 8932, 5049, 5241, 5886, 1393, 9413, 8872, 2560, 4636, 9004, 7586, 1461, 350, 2627, 2187, 7778, 8933, 351, 7097, 356, 4110, 1393, 4864, 1088, 3904, 5623, 8040, 7273, 1114, 4394, 4108, 7123, 8001, 5715, 7215, 7460, 5829, 9513, 1256, 4052, 1585, 1608, 3941]

for i in range(len(list1)):
    if list1[i] == list2[i]:
        print(list1[i])

Python
1
2
3
4
5
6
7
list1 = [8468, 4560, 3941, 3328, 7, 9910, 9208, 8400, 6502, 1076, 5921, 6720, 948, 9561, 7391, 7745, 9007, 9707, 4370, 9636, 5265, 2638, 8919, 7814, 5142, 1060, 6971, 4065, 4629, 4490, 2480, 9180, 5623, 6600, 1764, 9846, 7605, 8271, 4681, 2818, 832, 5280, 3170, 8965, 4332, 3198, 9454, 2025, 2373, 4067]
list2 = [9093, 2559, 9664, 8075, 4525, 5847, 67, 8932, 5049, 5241, 5886, 1393, 9413, 8872, 2560, 4636, 9004, 7586, 1461, 350, 2627, 2187, 7778, 8933, 351, 7097, 356, 4110, 1393, 4864, 1088, 3904, 5623, 8040, 7273, 1114, 4394, 4108, 7123, 8001, 5715, 7215, 7460, 5829, 9513, 1256, 4052, 1585, 1608, 3941]

i = 0
while list1[i] != list2[i]:
    i += 1
print(list1[i])
Avantage : on s'arrête dès qu'on a trouvé !

Inconvénient : on part du principe que ce nombre existe vraiment. Si on ne le trouve pas, on aura une erreur...

Exercice 4

Dans la liste

Python
lst = [2428970, 1518306, 4971405, 1690994, 7918102, 4030834, 8830131, 7514856, 7903128, 6307569, 6624056, 5260490, 6447835, 4598783, 9108626, 5045240, 4128269, 4460134, 2497873, 5076659, 8104003, 7604887, 7451976, 4136924, 5691945, 8726293, 7855592, 3562473, 8849129, 6488474, 5303587, 2606124, 5484044, 4559758, 7592232, 2211406, 9974334, 7988936, 7582946, 7668748, 1799997, 3837917, 3196209, 7064342, 2543765, 1182013, 7253381, 1153735, 1037391, 4375946, 4445821, 5965587, 6001887, 4162629, 5235783, 8716582, 4901175, 5445422, 1120005, 8332321, 7075046, 2194175, 5557300, 2887907, 5103214, 2520744, 5104399, 2065665, 3035703, 7890213, 1758301, 3407982, 1355453, 4896338, 7979392, 9671602, 9690721, 7423779, 7423780, 3080825, 6785783, 3836837, 7310931, 1857470, 3492507, 2823231, 1492310, 1911148, 9620515, 5564910, 7009452, 7464745, 9608747, 7267383, 6939140, 6556578, 3592267, 8135497, 4881660, 5346884, 6859150]
se cachent deux nombres consécutifs (comme les nombres 4 et 5 dans la liste [6, 2, 4, 5, 3] ). Pouvez-vous les trouver ?

Python
1
2
3
4
5
lst = [2428970, 1518306, 4971405, 1690994, 7918102, 4030834, 8830131, 7514856, 7903128, 6307569, 6624056, 5260490, 6447835, 4598783, 9108626, 5045240, 4128269, 4460134, 2497873, 5076659, 8104003, 7604887, 7451976, 4136924, 5691945, 8726293, 7855592, 3562473, 8849129, 6488474, 5303587, 2606124, 5484044, 4559758, 7592232, 2211406, 9974334, 7988936, 7582946, 7668748, 1799997, 3837917, 3196209, 7064342, 2543765, 1182013, 7253381, 1153735, 1037391, 4375946, 4445821, 5965587, 6001887, 4162629, 5235783, 8716582, 4901175, 5445422, 1120005, 8332321, 7075046, 2194175, 5557300, 2887907, 5103214, 2520744, 5104399, 2065665, 3035703, 7890213, 1758301, 3407982, 1355453, 4896338, 7979392, 9671602, 9690721, 7423779, 7423780, 3080825, 6785783, 3836837, 7310931, 1857470, 3492507, 2823231, 1492310, 1911148, 9620515, 5564910, 7009452, 7464745, 9608747, 7267383, 6939140, 6556578, 3592267, 8135497, 4881660, 5346884, 6859150]

for i in range(len(lst)-1): # Il faut s'arrêter à l'avant-dernier élément de la liste
    if lst[i] + 1 == lst[i+1]:
        print(lst[i], lst[i+1])

5. Modification d'une liste⚓︎

En Python, les objets de type List sont modifiables (on emploie le mot mutables). Et c'est souvent une bonne chose, car des listes peuvent évoluer après leur création. Lorsqu'on souhaitera figer le contenu d'une liste (pour des raisons de sécurité du code essentiellement), on utilisera alors le type Tuple, qui sera vu ultérieurement.

5.1 Modification d'un élément existant⚓︎

Il suffit d'écraser la valeur actuelle avec une nouvelle valeur

Exemple fondateur n°6 ❤

Python
>>> famille = ["Bart", "Lisa", "Maggie"]
>>> famille[0] = "Bartholomew" # oui, c'est son vrai nom
>>> famille
['Bartholomew', 'Lisa', 'Maggie']   

5.2 Ajout d'un élement à la fin d'une liste : la méthode append() ⭐⚓︎

Exemple fondateur n°7 ❤

Python
>>> famille = ["Bart", "Lisa", "Maggie"]
>>> famille.append("Milhouse")
>>> famille
['Bart', 'Lisa', 'Maggie', 'Milhouse']  

Remarques :

  • La méthode append() rajoute donc un élément à la fin de la liste.
  • Dans beaucoup d'exercices, on part d'une liste vide [] que l'on remplit peu à peu avec des append().
  • (HP) Il est possible d'insérer un élément à la position i avec la méthode insert :
    Python
    >>> famille = ["Bart", "Lisa", "Maggie"]
    >>> famille.insert(1, "Nelson") # on insère à la position 1
    >>> famille
    ['Bart', 'Nelson', 'Lisa', 'Maggie']
    
Exercice 5

Construire une liste contenant tous les nombres inférieurs à 100 qui sont divisibles par 7.

Python
1
2
3
4
lst = []
for n in range(1, 101):
    if n % 7 == 0:
        lst.append(n)
Exercice 6

On considère la liste temp = [4, -5, 8, 10, -1, -2, 7, 13].
Construire la liste temp_pos qui ne contient que les éléments positifs de temp.

Python
1
2
3
4
5
6
temp = [4, -5, 8, 10, -1, -2, 7, 13]

temp_pos = []
for t in temp:
    if t > 0:
        temp_pos.append(t)

5.3 Suppression d'un élément d'une liste ...⚓︎

5.3.1 ... par la méthode remove()⚓︎

Exemple fondateur n°7 ❤

Python
>>> famille = ['Bart', 'Nelson', 'Lisa', 'Maggie']
>>> famille.remove("Nelson")
>>> famille
['Bart', 'Lisa', 'Maggie']

Remarques :

  • Attention, remove n'enlève que la première occurrence de l'élément désigné. S'il y en a d'autres après, elles resteront dans la liste :
    Python
    >>> lst = [3, 1, 4, 5, 1, 9, 4]
    >>> lst.remove(4)
    >>> lst
    [3, 1, 5, 1, 9, 4]
    
  • Si l'élément à supprimer n'est pas trouvé, un message d'erreur est renvoyé :
    Python
    >>> lst = [3, 1, 4, 5, 1, 9]
    >>> lst.remove(2)
    Traceback (most recent call last):
      File "<pyshell>", line 1, in <module>
    ValueError: list.remove(x): x not in list
    

5.3.2 ... par l'instruction del⚓︎

L'instruction del (qui n'est pas une fonction) permet de supprimer un élément en donnant son indice.

Python
>>> maliste = [8, 4, 2, 5, 7]
>>> del maliste[3]
>>> maliste
[8, 4, 2, 7]
Exercice 7

Exercice de la BNS.

6. Construction d'une liste d'éléments identiques⚓︎

Il est souvent pratique d'initialiser une liste de taille donnée, souvent en la remplissant de 0.

Imaginons par exemple que nous souhaitions une liste de taille 26 remplie de 0. Il est possible de faire comme ceci :

Python
1
2
3
lst = []
for _ in range(26):
    lst.append(0)

mais on préfèrera ce code :

Exemple fondateur n°8 ❤

Python
>>> lst = [0]*26

qui produira la liste [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Exercice 8

Que fait le code ci-dessous ?

Python
texte = "cet texte est prodigieusement ennuyeux"

def rang(lettre):
    return ord(lettre) - 97

compt = [0]*26
for lettre in texte :
    if lettre != " " :
        compt[rang(lettre)] += 1

À l'issue de ce code la variable compt contient [0, 0, 1, 1, 9, 0, 1, 0, 2, 0, 0, 0, 1, 3, 1, 1, 0, 1, 2, 5, 3, 0, 0, 2, 1, 0], qui correspond au nombre d'occurences de chaque lettre : 0 fois la lettre 'a', 0 fois la lettre 'b', 1 fois la lettre 'c', etc.

Ce genre de comptage se fera de manière beaucoup plus efficace et élégante avec la structure de dictionnaire.

7. Construction d'une liste en compréhension ⭐ ⭐ ⭐⚓︎

C'est une grande caractéristique du langage Python (même si ce n'est pas une exclusivité) : la méthode de liste en compréhension propose une manière élégante, rapide et naturelle pour créer des listes.

7.1 «en compréhension», pourquoi ?⚓︎

Cette expression vient des mathématiques. On dit qu'on définit un sous-ensemble par compréhension lorsqu'on part d'un ensemble plus grand dont on ne garde que les éléments vérifiant une certaine propriété.

On pourrait par exemple définir les élèves de Première NSI de cette manière :

«élèves du lycée inscrits en classe de Première ayant choisi la spécialité NSI»

On part d'un ensemble large (les élèves du lycée) qu'on va ensuite réduire par des caractérisations spécifiques : être un élève de Première, puis avoir choisi la spécialité NSI.

7.2 Premier exemple⚓︎

Exemple fondateur n°9 ❤

Imaginons que nous possédons une liste data de températures, dont nous ne voulons garder que celles strictement supérieures à 20.

Python
>>> data = [17, 22, 15, 28, 16, 13, 21, 23]
>>> good = [t for t in data if t > 20]
>>> good
[22, 28, 21, 23]

Explications :

image

7.2.1 le filtre éventuel⚓︎

C'est lui qui donne tout son sens à cette méthode : il permet de ne garder que certaines valeurs. Il est pourtant éventuel : que se passe-t-il s'il n'y a pas de filtre ?

Python
>>> data = [17, 22, 15, 28, 16, 13, 21, 23]
>>> good = [t for t in data]
>>> good
[17, 22, 15, 28, 16, 13, 21, 23]
On se retrouve évidemment avec une nouvelle liste qui contient exactement les éléments de la liste de départ, ce qui n'est pas très intéressant. Pourtant les listes en compréhension sans filtre sont très fréquentes, nous le verrons plus loin.

Exercice 9

On considère la variable phrase = 'Bonjour les vacances sont finies' et la variable voyelles = 'aeiouy'.

Construire en compréhension la liste liste_voyelles qui contient toutes les voyelles présentes dans la variable phrase.

Python
>>> phrase = 'Bonjour les vacances sont finies'
>>> voyelles = 'aeiouy'
>>> liste_voyelles = [lettre for lettre in phrase if lettre in voyelles]
>>> liste_voyelles
['o', 'o', 'u', 'e', 'a', 'a', 'e', 'o', 'i', 'i', 'e']

7.2.2 l'ensemble de départ⚓︎

C'est à partir de lui que va se construire notre liste. Pour l'instant, cet ensemble de départ a toujours été de type list.

Cet ensemble peut être aussi donné à partir de l'instruction range(). Souvenons-nous de l'exercice 4 : «Construire une liste contenant tous les nombres inférieurs à 100 qui sont divisibles par 7.».

Une solution possible était :

Python
1
2
3
4
lst = []
for n in range(1, 101):
    if n % 7 == 0:
        lst.append(n)

Ce code peut maintenant s'écrire très simplement en une seule instruction :

Exemple fondateur n°10 ❤

Python
>>> lst = [n for n in range(1,101) if n % 7 == 0]
>>> lst
[7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77, 84, 91, 98]

7.2.3 la valeur à garder⚓︎

Pour l'instant, nous avons procédé à des filtres sur des ensembles existants, sans modifier la valeur filtrée (la valeur à garder).
Les listes en compréhension deviennent encore plus intéressantes lorsqu'on comprend qu'il est possible de modifier la valeur filtrée :

Exemple fondateur n°11 ❤

Python
>>> lst_carres = [t**2 for t in range(1,10)]
>>> lst_carres
[1, 4, 9, 16, 25, 36, 49, 64, 81]
Exercice 10
  1. On considère la fonction mathématique \(f(x) = 2 \times x+3\). Coder la fonction f.
  2. Créer (en compréhension) une liste contenant l'image des entiers de 1 à 10 par la fonction \(f\).
Python
1
2
3
4
def f(x):
    return 2*x + 3

lst = [f(x) for x in range(1, 11)]
Exercice 11

On considère la liste lst = [51, 52, 66, 91, 92, 82, 65, 53, 86, 42, 79, 95]. Seuls les nombres entre 60 et 90 ont une signification : ce sont des codes ASCII (récupérables par la fonction chr ).
Créer (en compréhension) une liste sol qui contient les lettres correspondants aux nombres ayant une signification.

Python
>>> lst = [51, 52, 66, 91, 92, 82, 65, 53, 86, 42, 79, 95]
>>> decode = [chr(k) for k in lst if k > 60  and k < 90]
>>> decode
['B', 'R', 'A', 'V', 'O']

7.2.4 Des exercices⚓︎

Exercice 12
Python
tableau = [i for i in range(5, 15)]
  • tableau = [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
  • tableau = [i, i, i, i, i, i, i, i, i, i]
  • tableau = [0, 0, 0, 0, 0, 0, 0, 0, 0]
  • tableau = [5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
  • ❌ La valeur 15 est exclue.
  • ❌ i prend les valeurs de l'intervalle.
  • ❌ La valeur n'est pas contante.
  • ✅ i prend tour à tour les valeurs de 5 jusqu'à 14.
Exercice 13

Ecrire en compréhension la liste [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5] qui contient 20 fois l'entier 5.

Puzzle

Exercice 14

Compléter le script ci-dessous :

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5

ENCRYPTION_TOKEN

Solution & Remarques

🐍 Proposition de correction
cents = [100 for k in range(10)]
entiers = [k for k in range(1, 11)]

Remarques :

Pas de remarque particulière pour cet exercice

ENCRYPTION_TOKEN

Solution
Python
# un tableau cents en compréhension qui contient 10 entiers 100.
cents = [100 for k in range(10)]

# un tableau entiers en compréhension qui contient les 10 entiers entre 1 et 10 compris.
entiers = [k for k in range(1, 11)]

On peut aussi utiliser des chaînes de caractères :

Testez

Exécuter le script ci-dessous :

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Exercice 15

Ecrire en compréhension : ['NSI-1', 'NSI-2', 'NSI-3', 'NSI-4', 'NSI-5', 'NSI-6', 'NSI-7', 'NSI-8', 'NSI-9', 'NSI-10']

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Solution
Python
mon_tab = ["NSI-"+ str(i) for i in range(1, 11)]
print(mon_tab)
Exercice 16

Compléter ci dessous le script : Ecrire en compréhension lst3 qui donne la liste de la somme des éléments de [2, 3, 1, 5] et de [4, 1, 7, 0]. On doit obtenir : [6, 4, 8, 5]

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Solution
Python
lst1 = [2, 3, 1, 5]
lst2 = [4, 1, 7, 0]
lst3 = [lst1[p] + lst2[p] for p in range(len(lst1))]
print(lst3)
Exercice 17

Répondre sur papier.

Donner les listes lst1, lst2, lst4, lst6 et lst7

Python
lst1 = [3 for i in range(4)]
lst2 = [4-i for i in range(3)]
lst3 = [1, 2, 3]
lst4 = [lst3[i]**2 for i in range(len(lst3))]
lst5 = ["a","b","c"]
lst6 = [lst5[i]*2 for i in range(len(lst5))]
lst7 = [elem*2 for elem in lst5]
Solution
Python
lst1 = [3, 3, 3, 3]
lst2 = [4, 3, 2]
lst4 = [1, 4, 9]
lst6 = ['aa', 'bb', 'cc']
lst7 = ['aa', 'bb', 'cc']

7.2.5 Utilisation plus élaborée des tableaux en compréhension⚓︎

Grâce à la construction par compréhension, il est possible d'appliquer un traitement (opération, fonction...) à chaque élément d'un tableau.

Testez

Exécuter le script ci-dessous :

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Exercice 18
Python
double = [i*2 for i in range(10)]
  • double = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  • double = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
  • double = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
  • double = [4, 8, 12, 16]
  • ❌ Un traitement est appliqué aux valeurs prises par i.
  • ❌ Le mauvais traitement est appliqué.
  • ✅ le tableau est composé du double de chaque valeur de l'intervalle [0,9]
  • ❌ Le tableau double doit contenir autant d'éléments que le tableau d'origine.
Exercice 19

Compléter pour obtenir la liste dizaines = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]

Puzzle

7.2.6 Appliquer un filtre⚓︎

La construction par compréhension permet d'appliquer un filtre à une structure de données de départ, afin de ne garder que certains éléments. On utilise pour cela une condition précédée du mot-clé if.

On peut ainsi créer un tableau qui ne conserve que les nombres pairs d'un tableau initial.

Python
>>> tableau = [0, 1, 6, 5, 4, 11, 12, 23, 26]
>>> pairs = [p for p in tableau if p%2 == 0]
>>> pairs
[0, 6, 4, 12, 26]
Exercice 20
Python
tableau = [i for i in range(5, 15)]
nouveau_tableau = [j for j in tableau if j < 10]
  • nouveau_tableau = [5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
  • nouveau_tableau = [5, 6, 7, 8, 9]
  • nouveau_tableau = [9, 8, 7, 6, 5]
  • nouveau_tableau = [10, 11, 12, 13, 14]
  • ❌ La condition entraine la sélection de certaines valeurs.
  • ✅ on sélectionne tous les éléments du tableau inférieurs à 10.
  • ❌ Les éléments conservent l'ordre dans lequel il se trouvent dans le tableau initial.
  • ❌ La condition indique les éléments qui sont conservés.
Exercice 21

Créer en compréhension la liste des carrés des nombres de la liste nombresqui sont négatifs.

Puzzle

Exercice 22

Compléter le script ci-dessous :

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Solution
Python
# un tableau positifs en compréhension qui contient 
# les nombres réels positifs du tableau nombres
nombres = [1, 0, -2, 9, -5, 4, -7, 5, -8]
positifs = [k for k in nombres if k > 0]

# un tableau voyelle_phrase en compréhension qui ne contient que les voyelles 
# contenues dans la chaine de caractère phrase
phrase = "je ne suis pas sans voix !"
VOYELLES = "aeiouy"
voyelle_phrase = [caractere for caractere in phrase if caractere in VOYELLES]

8. Un phénomène inquiétant : la copie de liste⚓︎

une copie un peu trop parfaite

Observez le code ci-dessous, réalisé sans trucage.

Python
>>> listA = [1, 2, 3]
>>> listB = listA
>>> listA.append(7)
>>> listB
[1, 2, 3, 7]
>>> listB.append(8)
>>> listA
[1, 2, 3, 7, 8]

Tout se passe comme si les listes listA etlistB étaient devenus des clones «synchronisés» depuis l'affectation listB = listA.

Analyse grâce à PythonTutor

L'illustration de PythonTutor nous donne la clé de l'énigme : image

listA etlistB sont en fait un seul et même objet.

Comment en avoir le cœur net ? En observant leur adresse-mémoire, disponible grâce à la fonction id :

Python
>>> id(listA)
140485841327616
>>> id(listB)
140485841327616

Ceci met en évidence que la métaphore du tiroir dont on se sert pour expliquer ce qu'est une variable est malheureusement inexacte. Une variable est une référence vers une adresse-mémoire. Si deux variables font référence à la même adresse-mémoire, alors elles sont totalement identiques: toute modification de l'une entraîne une modification de l'autre.

Pour en savoir plus sur les variables, vous pouvez revenir sur la partie optionnelle du cours sur les variables.

Mais alors, comment copier le contenu d'une liste vers une autre sans créer un clone ?⚓︎

Exemple fondateur n°12 ❤

Python
>>> listA = [3, 4, 5]
>>> listB = list(listA)
D'autres possibilités existent, comme listA.copy(), ou encore listA[::]...

Exercice 23

Effectuer les tests nécessaires pour prouver que l'exemple précédent a bien produit deux objets différents.

Python
>>> listA = [3, 4, 5]
>>> listB = list(listA)
>>> listA.append(9)
>>> listB
[3, 4, 5]
>>> id(listA)
140157471522368
>>> id(listB)
140157465797184

9. Tableaux à plusieurs dimensions : listes de listes⚓︎

Nous avons vu qu'une liste pouvait contenir des éléments de tous types : des entiers, des chaines des caractères... et pourquoi pas une liste qui contient des listes ?

9.1 Syntaxe⚓︎

Exemple fondateur n°12 ❤

La liste tab ci-dessous est composée de 3 listes qui elles-mêmes contiennent trois nombres :

Python
tab =  [[3, 5, 2],
        [7, 1, 4], 
        [8, 6, 9]]

  • tab[0][0] = 3
  • tab[0][1] = 5
  • tab[2][1] = 6
  • tab[1] = [7, 1, 4]

image

La liste a est composée de 3 éléments qui sont eux-même des listes de 3 éléments.

Exercice 24

On considère le jeu du Morpion (ou Tic-Tac-Toe) dont la surface de jeu vierge est representée par le tableau :
tab = [[' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]

Les premiers coups joués sont ceux-ci :

  • tab[1][1] = 'X'
  • tab[2][1] = 'O'
  • tab[2][2] = 'X'
  • tab[0][0] = 'O'

Quel coup doit maintenant jouer le joueur 'X' pour s'assurer la victoire ?

Python
tab[0][2] = 'X'
# ou
tab[1][2] = 'X'

9.2 Parcours d'une liste de listes⚓︎

Exemple fondateur n°13 ❤

  • Parcours par éléments :

    Python
    1
    2
    3
    for ligne in tab:
        for elt in ligne:
            print(elt)
    

  • Parcours par indice :

    Python
    1
    2
    3
    for i in range(3):
        for j in range(3):
            print(tab[i][j])
    

Exercice 25

On considère la liste m ('m' comme matrice) suivante :

m = [[1, 9, 4], [4, 1, 8], [7, 10, 1]]

Quelle est la somme de tous les nombres de la matrice m ?

Python
1
2
3
4
5
6
7
8
m = [[1, 9, 4], [4, 1, 8], [7, 10, 1]]

somme = 0
for ligne in m:
    for elt in ligne:
        somme += elt

print(somme)

ou bien

Python
1
2
3
4
5
6
7
8
m = [[1, 9, 4], [4, 1, 8], [7, 10, 1]]

somme = 0
for i in range(3):
    for j in range(3):
        somme += m[i][j]

print(somme)

9.3 Les listes de listes avec compréhensions imbriquées⚓︎

Pour construire un tableau de tableaux de même longueurs, on peut utiliser des compréhensions imbriquées.

Dans les exemples qui suivent nous appelerons matrice notre tableau de tableaux.

Python
>>> matrice = [[k for k in range(4)] for j in range(3)]
>>> matrice
[[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]]

Pour bien visualiser le tableau matrice que nous venons de créer, nous pouvons l'écrire de la façon suivante :

Python
    matrice = [ [0, 1, 2, 3],
                [0, 1, 2, 3],
                [0, 1, 2, 3] ]         
👉 Chaque élément de matrice correspond donc à une "ligne".

On peut considérer que matrice a quatre "colonnes". Par exemple la première colonne (de rang 0) est [0, 0, 0], et la dernière est [3, 3, 3].

💡 Il est possible d'extraire une ligne de matrice.

Python
>>> ligne_0 = matrice[0]
>>> ligne_0
[0, 1, 2, 3]

💡 Il est possible d'extraire une colonne de matrice.

Python
>>> colonne_2 = [ligne[2] for ligne in matrice]
>>> colonne_2
[2, 2, 2]
QCM
Python
matrice = [[j for i in range(4)] for j in range(4)]
  • matrice = [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]]
  • matrice = [[0, 0, 0, 0], [1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]]
  • matrice = [[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]]
  • matrice = [0, 1, 2, 3]
  • ❌ La valeur j est constante pour chaque ligne.
  • ✅ La valeur j prend la valeur 0 pour la première ligne, puis 1, etc.
  • ❌ La valeur j est constante pour chaque ligne.
  • ❌ Les constructions imbriquées engendrent un tableau de tableaux.
Puzzle

Créer en compréhension la matrice [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

Puzzle

À vous de jouer 1

Compléter le script ci-dessous :

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Solution
Python
# un tableau matrice_carre_10 en compréhension, matrice 10x10
# dont chaque ligne contient les entiers de 1 à 10.
matrice_carre_10 = [[k for k in range(1, 11)] for j in range(10)]

# un tableau ligne_5 en compréhension qui contient la colonne 5 (située à l'indice 4)
# de la matrice matrice_carre_10
colonne_5 = [ligne[4] for ligne in matrice_carre_10]

# un tableau diagonale en compréhension qui contient les éléments situés 
# à la 1ère colonne de la 1ère ligne,
# à la 2ème colonne de la 2ème ligne ... de la matrice carrée matrice_carre_10
diagonale = [matrice_carre_10[i][i] for i in range(len(matrice_carre_10))]
À vous de jouer 2

Compléter la fonction donne_colonne qui prend en paramètres une matrice matrice, un entier colonne et renvoie la liste des éléments de la colonne colonne.

Nous avons déjà réalisé cet exercice dans le chapitre : listes de listes. Vous devez ici absolument utiliser une liste en compréhension.

Par exemple :

Python Console Session
>>> m = [[1, 3, 4],
    [5, 6, 8],
    [2, 1, 3],
    [7, 8, 15]]
>>> donne_colonne(m, 0)
[1, 5, 2, 7]
>>> donne_colonne(m, 2)
[4, 8, 3, 15]
>>> 

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5

ENCRYPTION_TOKEN

Solution

🐍 Proposition de correction
1
2
3
4
5
6
7
def donne_colonne(matrice, n_colonne) -> list :
    """
    Entrée : matrice : une liste de listes, un entier n_colonne le numéro de la colonne
    Sortie : colonne : une liste dont les éléments sont les éléments de la colonne numéro n_colonne
    """
    colonne = [matrice[ligne][n_colonne] for ligne in range(len(matrice))]
    return colonne

ENCRYPTION_TOKEN

À vous de jouer 3

Compléter le script ci-dessous :

👉 Vous pouvez tester les ### en haut à droite de la fenêtre.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 4/4

ENCRYPTION_TOKEN

Solution & Remarques

🐍 Proposition de correction
def get_diag_2(lst) -> list :
    """
    Entrée : lst : une liste de listes de mêmes longueurs
    Sortie : diag_2 : une liste dont les éléments sont les éléments de la 2eme diagonale 
    (d'en haut  droite à en bas à gauche)
    >>> m = [ [1, 3, 4],
              [5 ,6 ,8],
              [2, 1, 3] ]
    >>> get_diag_2(m)
    [4, 6, 2]

    """

    indice_max = len(lst) - 1
    return [lst[i][indice_max-i] for i in range(len(lst))]

Remarques :

💡 On peut aussi utiliser le fait que le dernier élément d'une liste est d'indice -1, l'avant dernier d'indice -2 etc.

👉 Autre réponse possible :

Python
def get_diag_2(lst) -> list :
    """
    Entrée : lst : une liste de listes de mêmes longueurs.
    Sortie : diag_2 : une liste dont les éléments sont les éléments de la 2eme diagonale 
    (d'en haut  droite à en bas à gauche)
    >>> m = [ [1, 3, 4],
              [5 ,6 ,8],
              [2, 1, 3] ]
    >>> get_diag_2(m)
    [4, 6, 2]

    """

    return [lst[i][-1-i] for i in range(len(lst))]

ENCRYPTION_TOKEN

Solution
Python
def get_diag_2(lst) -> list :
"""
Entrée : lst : une liste de listes de mêmes longueurs
Sortie : diag_2 : une liste dont les éléments sont les éléments de 
la 2eme diagonale (de en bas à gauche à en haut  droite)
>>> m = [ [1, 3, 4],
        [5 ,6 ,8],
        [2, 1, 3] ]
>>> get_diag_2(m)
[4, 6, 2]

"""

indice_max = len(lst) - 1
return [lst[i][indice_max-i] for i in range(len(lst))]

10 Exercices⚓︎

1. Exercices variés⚓︎

Exercices variés

😀 La correction est arrivée ...

2. "π à Monte-Carlo"⚓︎

Sujet - π à Monte-Carlo

La méthode de Monte-Carlo est un ensemble de méthodes algorithmiques visant à déterminer la valeur approchée d'une constante en utilisant des procédés aléatoires.

On peut utiliser cette méthode afin de déterminer une valeur approchée de \(\pi\). L'idée est la suivante :

  • on considère un carré de \(2\) unités de côtés. Son aire vaut donc \(4\) ;
  • on considère un disque de rayon \(1\) centré au centre du carré. Son aire vaut donc \(\pi \times 1^2=\pi\) ;
  • on génère un grand nombre de points aléatoires répartis de façon uniforme dans le carré.

Il reste alors à compter le nombre de points à l'intérieur du disque. On peut montrer que leur fréquence tend vers \(\frac{\pi}{4}\) quand le nombre de points aléatoires devient très grand.

Une valeur approchée de \(\pi\) est donc :

\[\pi \approx 4 \times \frac{\text{nombre de points dans le disque}}{\text{nombre de points dans le carré}}\]

On observe ci-dessous le carré de départ ainsi que de nombreux points. On a représenté de couleur différente ceux qui sont dans le cercle et ceux qui n'y sont pas.

Méthode de Monte-Carlo

On se donne donc :

  • une liste de nb_points aléatoires, tous dans le carré décrit ci-dessus. Cette liste est nommée points et chaque point est représenté par ses coordonnées. Par exemple [(-0.5313, 0.0936), (0.9638, 0.3577), ...].

  • une fonction distance_origine prenant en argument les coordonnées x et y d'un point et renvoyant sa distance à l'origine du repère (et donc au centre du cercle)

On demande d'extraire la liste des points situés dans le cercle à l'aide d'une liste en compréhension.

La fonction random

Le module random de Python propose une fonction random qui génère des nombres aléatoires uniformément répartis entre 0 et 1.

👉 On a donc 2 * random() qui est compris entre 0 et 2

👉 On en déduit que 2 * random() - 1 est compris entre -1 et 1.

À vous de jouer

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Solution

Pour ne pas surcharger le site, nous avons choisi ici nb_points = 1000. Chez vous, sur votre prpre éditeur Python, vous pouvez tester avec nb_points = 100_000, pour obtenir une meilleure précision.

Python
from math import sqrt
from random import random

def distance_origine(x, y):
    return sqrt(x*x + y*y)

nb_points = 1000
points = [(2 * random() - 1, 2 * random() - 1) for _ in range(nb_points)]
dans_cercle = [p for p in points if distance_origine(p[0], p[1]) <= 1]

# Affiche une valeur approchée de pi
approximation = 4 * len(dans_cercle) / nb_points
print("Pi est environ égal à : ", approximation)