Listes

Les listes sont la structure principale lorsque l'on veut stocker plusieurs objets. La liste est un conteneur dont on peut accéder les éléments un à un.

Utilisez la console de https://console.basthon.fr/ pour exécuter les divers exemples et exercices

Une liste est une classe python.

Création et références

On crée un objet de type liste comme d'habitude, en mettant son nom suivie de parenthèses :

x = list()

Comme la liste fait partie des conteneurs les plus utilisés de python, on peut aussi directement créer une liste avec des crochets :

x = []

Les deux instructions précédentes ont créées des liste vides. L'état de l'espace de nommage après l'affectation est :

liste vide

mais on peut aussi directement créer une liste avec des objets dedans :

x = [1, 4, "douze"]

L'exemple précédent à créé une liste de nom x qui contient l'entier 1 en 1ère position, l'entier 4 en 2ème position et la chaîne de caractères "douze" en troisième position. L'espace de nommage est alors :

liste 3 éléments

On voit que les 3 objets ne sont pas contenus dans la liste, elle ne possède qu'une référence vers eux. Explicitons ça :

réponse = 42
x = [réponse]

Ce qui donne comme espace de nommage :

liste 1 élément

Le premier élément de la liste est aussi associé à la variable réponse.

On peut aussi, bien sur faire ce genre de choses :

réponse = 42
x = [réponse]
y = [x, 'un élément']

L'espace de nommage est alors :

liste 2 éléments

Remarquez qu'une liste peut avoir une référence à autre liste.

Affichage

On peut afficher une liste en utilisant la fonction print :

print([1, 4, "douze"])

Accès à un élément d'une liste

On accède à un élément de la liste en faisant suivre le nom de la liste par des [] et en mettant l'index voulu dans les crochets. Si x = [1, 4, "douze"], alors :

>>> x[0]
1
>>> x[1]
4
>>> x[2]
'douze'

Le nombre d'élément d'un conteneur, comme une liste, peut être donné par la fonction len. Pour l'exemple précédent :

>>> len(x)
3

Une chaîne de caractère, bien qu'elle ne soit pas une liste stricto sensu peut-être considérée comme une liste composée de caractères : on peut accéder à un caractère particulier de la chaîne comme on le ferait avec une liste.

Quel est la treizième lettre du mot "anticonstitutionnellement" ?

solution

>>> "anticonstitutionnellement"[12]
't'

On peut aussi commencer par la fin, d'index -1 :

>>> x[-1]
'douze'
>>> x[-2]
4
>>> x[-3]
1

Pour la chaîne "PYTHON" :

itérable P Y T H O N
numérotation à partir du début 0 1 2 3 4 5
numérotation à partir de la fin -6 -5 -4 -3 -2 -1

Quel est la quatrième lettre avant la fin du mot "anticonstitutionnellement" ?

solution

>>> "anticonstitutionnellement"[-4]
'm'

Modification d'une liste

Modifier une liste se fait soit en modifiant directement un objet de la liste, soit en ajoutant/supprimant un ou plusieurs de ses éléments.

Modifier un élément

On accède à un élément qu'on modifie. Par exemple :

x = [1, 4, "douze"]
print(x)
x[2] = 12
print(x)

L'élément d'une liste est considérée comme une variable que l'on peut modifier. Il est cependant impossible de modifier un élément qui n'existe pas. Par exemple, le code suivant produira une erreur :

x = [1, 4, "douze"]
x[3] = 12

Ajout d'un élément

Nous utiliserons essentiellement deux façons d'ajouter des éléments à une liste, tous les deux utilisant des méthodes des objets de type liste.

Pour ajouter des éléments à une liste, nous utiliserons les méthodes :

Suppression d'un élément

On peut utiliser la commande del pour supprimer un indice d'une liste~: l'instruction del l[1] supprime de la liste de nom l l'indice 1. L'objet associé au nom l est modifié, il n'est plus que de longueur 2.

>>> x = [1, 4, "douze"]
>>> del x[1]
>>> x
[1, 'douze']

Création

On a déjà vu la création directe d'une liste :

>>> x = [1, 4, "douze"]

Il existe d'autres façons, indirectes, de créer des listes, utile lorsque les listes sont issues de boucles.

Avec range

La fonction range qui produit des itérateurs peut également permettre de créer des listes.

Par exemple :

>>> list(range(5))
[0, 1, 2, 3, 4]

Crée une liste avec les 5 premiers entiers.

La fonction range ne crée pas de listes mais un itérateur.

>>> range(5)
range(0, 5)

N'est pas une liste.

Pour finir, un petit exercice :

Créez avec une boucle for la liste des 10 premiers carrés (de $0^2$ à $9^2$).

solution

l = []
for i in range(10):
    l.append(i ** 2)

Avec une list comprehension

l = [i ** 2 for i in range(10)]

Créez avec une list comprehension une liste contenant tous les entiers de 0 à 10.

solution

>>> l = [i for i in range(11)]
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Créez avec une list comprehension une liste contenant toutes les sommes $i + j$ avec i allant de 0 à 10 et j allant de 2 à 5.

solution

>>> l = [i + j for i in range(11) for j in range(2, 6)]
>>> l
[2, 3, 4, 5, 3, 4, 5, 6, 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, 8, 9, 7, 8, 9, 10, 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, 12, 13, 11, 12, 13, 14, 12, 13, 14, 15]

Créez avec une list comprehension une liste contenant toutes les sommes $i + j$ avec les i pairs pour les entiers allant de 0 à 10 et j allant de 2 à 5.

solution

>>> l = [i + j for i in range(11) if i % 2 == 0 for j in range(2, 6)]
>>> l
[2, 3, 4, 5, 4, 5, 6, 7, 6, 7, 8, 9, 8, 9, 10, 11, 10, 11, 12, 13, 12, 13, 14, 15]

Créez avec une list comprehension une liste contenant toutes les sommes $i + j$ avec les i pairs pour les entiers allant de 0 à 10 et j allant de 2 à 5 si $j-i$ est négatif ou nul.

solution

>>> l = [i + j for i in range(11) if i % 2 == 0 for j in range(2, 6) if j-i <= 0]
>>> l
[4, 6, 7, 8, 8, 9, 10, 11, 10, 11, 12, 13, 12, 13, 14, 15]

Itérer sur une liste

En tant que conteneur, une liste est un itérable. Elle peut peut donc faire partie d'une instruction for :

l = ["bonjour", "tout", "le", "monde", "!"]
for mot in l:
    print(mot)

Présence

Utiliser l'opérateur in est très utile avec les listes.

>>> l = ["bonjour", "tout", "le", "monde", "!"]
>>> "bonjour" in l
True
>>> 42 in l
False

Listes classiques

Quelques listes sont souvent demandées. Voici les moyens en python de les créer.

Listes d'entiers successifs

On utilise fonction range en combinaison avec le créateur de liste list() qui peut prendre un itérable en paramètre.

Par exemple pour la liste des 10 premiers entiers :

>>> L = list(range(10))
>>> L
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Créez la liste des entiers pair allant de 22 à 42 (inclut).

solution

>>> L = list(range(22, 43, 2))
>>> L
[22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42]

Créez une liste d'environ 15 entiers répartis équitablement entre 0 et 99.

solution

>>> L = list(range(0, 100, 100 // 15))
>>> L
[0, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96]

Listes d'entiers décroissants

On peut utiliser la même technique que précédemment. Par exemple, la liste des 10 premiers entiers décroissant :

>>> L = list(range(9, -1, -1))
>>> L
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

Mais souvent, on utilise la méthode des listes reverse qui renverse une liste :

>>> L = list(range(10))
>>> L.reverse()
>>> L
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

A noter qu'il existe aussi la fonction reversed qui rend un itérateur sur les éléments d'un itérable en paramètre, du dernier au premier. On peut donc aussi l'utiliser pour créer une liste duale d'une liste donnée :

>>> L = list(reversed(range(10)))
>>> L
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

Listes aléatoires

Mélanger une liste peut se faire avec le module random de python. Nous verrons les modules plus tard, mais par soucis de complétion, utilisons le ici pour créer des listes aléatoires.

Par exemple, la liste de 10 premiers entiers mélangés :

>>> import random
>>> L = list(range(10))
>>> random.shuffle(L)
>>> L
[3, 1, 4, 9, 6, 2, 0, 7, 8, 5]

Notez que la fonction random.shuffle ne rend rien. Elle mélange la liste passée en paramètre.

Ou l'utilisation de random.randrange pour créer des liste d'entiers aléatoires. Par exemple une liste de 10 nombres valant 0 ou 1 de façon aléatoire :

>>> L = [random.randrange(2) for i in range(10)]
>>> L
[1, 1, 0, 1, 1, 0, 1, 0, 1, 0]

Arrays du module numpy

Le module numpy possède de nombreuses fonction permettant de manipuler des tableaux. Ce ne sont pas stricto sensu des listes puisque leur type est array mais on peut souvent utiliser des arrays à la place des listes et réciproquement.

Utilisez la fonction numpy.random.randint pour créer un array de 10 entiers pris aléatoirement entre 3 et 9.

solution

>>> import numpy as np
>>> np.random.randint(3, 10, size=10)
array([4, 6, 4, 7, 6, 5, 6, 7, 8, 5])

Même liste, noms différents

Attention aux effets de bords !

Modifier un objet le modifie quelque soit sont nom.

Considérez l'exemple suivant :

>>> x = [1, 4, "douze"]
>>> y = x
>>> x[1] = 42
>>> x
[1, 42, 'douze']

Que vaut y ?

solution

>>> y
[1, 42, 'douze']

x et y sont le même objet sous deux noms différents.

Liste de listes

On peut créer des matrices facilement en utilisant des listes de listes. Considérez l'exemple suivant :

M = [[1, 2, 3], [4, 4, 6]]

On a crée une variable M qui contient une liste de 2 listes : c'est une matrice à 2 lignes et 3 colonnes.

La façon la plus sûre de fabriquer des listes de listes est de le faire avec des boucles for. Par exemple, pour créer une matrice M à 4 lignes et 5 colonnes ne possédant que des 1 :

M = []
for i in range(4):  # lignes
    ligne = []
    for j in range(5): # colones
        ligne.append(1)
    M.append(ligne)

Créez la matrice identité à 5 ligne et 5 colonnes.

solution

M = []
for i in range(5):  # lignes
    ligne = []
    for j in range(5): # colones
        if i == j:
            ligne.append(1)
        else:
            ligne.append(0)
    M.append(ligne)

Si on se sent plus hardi, on pourra utiliser des list comprehension :

Créez la matrice M à 4 lignes et 5 colonnes ne possédant que des 1 avec une unique list comprehension.

solution

>>> M = [[1 for c in range(5)] for l in range(4)]
>>> M
[[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]

On peut aussi utiliser des list comprehension pour créer des matrices plus compliquée, mais il faut souvent ruser car on n'a le droit qu'à une unique instruction par liste compréhension.

Créez la matrice identité à 5 ligne et 5 colonnes avec une unique list comprehension. Il pourra être utile de se rappeler le AND/OR trick avant de résoudre cet exercice.

solution

>>> M = [[(((l == c) and 1) or 0) for c in range(5)] for l in range(5)]
>>> M
[[1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, 0], [0, 0, 0, 0, 1]]

Copie

D'une liste

On utilise le nom de la classe list qui prend en paramètre un itérable pour créer une liste. Par exemple pour créer une copie de la liste x = [1, 2, 13] :

>>> x = [1, 2, 13]
>>> y = list(x)

Il est pratique de copier une liste car ensuite on peut modifier une liste sans peur des effets de bords :

>>> y[1] = 42
>>> x
[1, 2, 13]
>>> y
[1, 42, 13]

Alors que :

>>> x = [1, 2, 13]
>>> y = x
>>> y[1] = 42
>>> x
[1, 42, 13]
>>> y
[1, 42, 13]

D'une sous-liste

En anglais, cette technique est appelée slicing (des tranches).

On peut copier une partie d'une liste. Pour copier la liste l à partir de l'indice i jusqu'à l'indice j avec un pas de k par exemple : l[i:j:k]

Il n'est pas nécessaire de renseigner tous les champs. Si $l = [l_0, \dots, l_{n-1}]$, alors :

que donne l[::3] ou l[1::5] pour la liste [3, 6, 9, 12, 15, 18, 21, 24, 27, 30] ?

solution

>>> l = list(range(3, 31, 3))
>>> l[::3]
[3, 12, 21, 30]
>>> l[1::5]
[6, 21]

Le slicing permet aussi de remplacer directement la partie de liste si on procède à une affectation. Par exemple :

>>> l = list(range(10))
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> l[2:4] = ["deux", "trois"]
>>> l
[0, 1, 'deux', 'trois', 4, 5, 6, 7, 8, 9]

Méthodes des listes

Les méthodes de listes, comme les méthodes de chaînes de caractères, sont très utiles. A défaut de les apprendre par cœur, sachez retrouver la documentation pour voir si ce que vous cherchez à faire n'est pas déjà fait.

Par exemple pour ajouter ou supprimer des éléments d'une liste :

Attention à remove, extend ou pop qui ne font pas ce qu'on croit qu'elle font.

Que font-elles ?

solution

La réponse se trouve dans la documentation :

  • remove supprime le premier élément trouvé, pas tous
  • extend ajoute les éléments d'une liste passée en paramètre à la la liste à gauche du .
  • pop supprime le dernier élément de la liste et le rend

Il existe aussi de nombreuses méthodes de chaines de caractères qui utilisent des listes. Citons en deux :

Attention cependant lorsque vous utilisez des méthodes :

Certaines méthodes ne modifient la liste d'autre produisent de nouvelles liste. LIsez bien la documentation associée à la méthode pour l'utiliser correctement.sur lequel elle est appliquée.

Par exemple la méthode insert modifie la liste alors que index ne le fait pas :

Testez le code suivant pour voir la différence ;

ma_liste = list(range(5))
ma_liste.insert(2, "coucou")
un_indice = ma_liste.index("coucou")
print(un_indice)
print(ma_liste[un_indice])

Utilisez les méthodes sort et reverse (qui modifient les listes) pour résoudre l'exercice suivant :

Créez une liste de 20 entiers aléatoire allant de 1 à 10.

  1. afficher cette liste à l'écran
  2. triez cette liste puis affichez là à nouveau
  3. retournez la liste obtenue en 2 puis affichez là à nouveau

solution

import random

L = [random.randrange(11) for i in range(20)]
print(L)

L.sort()
print(L)

L.reverse()
print(L)

Lambda et listes

Les fonction lambda permettent d'être utilisée directement dans des méthodes de liste. Par exemple avec le paramètre key de la méthode de liste sort. Considérons la liste l :

l = [["au revoir", 2], ["bonjour", 1]]

Si on cherche à trier l, la liste sera triée en comparant le 1er élément de chaque liste :

l.sort()

print(l)  # donnera [['au revoir', 2], ['bonjour', 1]]

Si l'on veut trier sur le deuxième élément de chaque liste, on utilise le paramètre key qui est une fonction. Les éléments $x$ de la liste seront triés selon $key(x)$ plutôt que $x$ :

def second(x):
    return x[1]

l.sort(key=second)

print(l)  # donnera [['bonjour', 1], ['au revoir', 2]]

Que donnerait le tri si la fonction second avait été définie comme ceci :

def second(x):
    return 1 / x[1]

solution

def second(x):
    return 1 / x[1]

l = [["au revoir", 2], ["bonjour", 1]]

l.sort(key=second)

print(l)

Utiliser une fonction lambda permet de raccourcir le code précédent tout en le gardant très clair :

l = [["au revoir", 2], ["bonjour", 1]]

l.sort(key=lambda x: x[1])

print(l)  # donnera [['bonjour', 1], ['au revoir', 2]]

Opérateurs de listes

Comme pour les chaines de caractères :

Par exemple :

>>> [1, 4, "douze"] + [42]
[1, 4, 'douze', 42]
>>> [1, 4, "douze"] * 3
[1, 4, 'douze', 1, 4, 'douze', 1, 4, 'douze']

Remarquez que :

N'utilisez pas l'opérateur * pour créer des matrices. C'est le même objet qui est dupliqué. Si les objets dupliqué sont des types de bases, aucun problème mais si le type est un objet modifiable comme un conteneur, cela peut se finir en catastrophe. Ainsi :

>>> M = [[0, 0, 0]] * 3
>>> M
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]

M est constituée de 3 fois la même liste :

>>> M[1][1] = 1
>>> M
[[0, 1, 0], [0, 1, 0], [0, 1, 0]]

Cas particulier des chaines de caractères

Une chaîne de caractères peut être vue comme un conteneur de caractères. On peut donc accéder à un caractère particulier comme une liste :


>>> "abcdefghijklmnopqrstuvwxyz"[2]
'c'

Ou même utiliser des slices de liste :

>>> "abcdefghijklmnopqrstuvwxyz"[2:15:4]
'cgko'

En revanche, il est impossible de modifier une chaîne :

>>> x = "Francois"
>>> x[4] = "ç"
Traceback (most recent call last):
  File "<python-input-4>", line 1, in <module>
    x[4] = "ç"
    ~^^^
TypeError: 'str' object does not support item assignment
>>>

Entraînons nous un peut à manipuler les chaînes de caractères sous la forme d'un conteneur en reprenant le 27ème nombre de Mersenne sous sa forme chaîne de caractères : m27 = str(2 ** 44497 - 1).

Quels sont les 10 premiers chiffres de m27 ?

solution

str(m27)[:10]

Quels sont les 10 derniers chiffres de m27 ?

solution

str(m27)[-10:]

Est-ce que m27 est un palindrome ?

solution

str(m27) == str(m27)[::-1] (s[::-1] renverse la chaîne)

En revanche, il est interdit de modifier une chaine de caractère :

>>> x = "chaine"
>>> x[0] = "C"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

Enfin on ne le répètera jamais assez, python vient avec tout un tas de méthodes utilitaires permettant de résoudre nombre d'opérations courantes. Utilisez la documentation sur les méthodes de chaînes en python pour résoudre les exercices suivants :

Index de la première occurrence de 1234 dans m27. Et de la deuxième ?

solution

  • str(m27).find('1234')
  • str(m27).find('1234', 19260 + 1) : la première occurrence est à l'indice 19260, on cherche donc après.
  • on peut faire en une ligne : str(m27).find('1234', str(m27).find('1234') + 1)