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 :
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 :
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 :
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 :
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
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
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 :
-
append
qui ajoutent un élément en fin de liste :>>> x = [1, 4, "douze"] >>> x.append("a la fin") >>> x [1, 4, 'douze', 'a la fin']
-
insert
qui permettent d'ajouter un élément avant un indice passé en paramètre. Dans l'exemple, on ajoute un élément avant le l'élément d'indice 0, c'est à dire au début :>>> x = [1, 4, "douze"] >>> x.insert(0, "au debut") >>> x ['au debut', 1, 4, 'douze'] >>>
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
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
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
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
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
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
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
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 array
s à 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
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
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 1ère ligne de la matrice est
M[0]
et la secondeM[1]
- l'élément à la 1ère ligne et deuxième colonne s'écrit :
M[0][1]
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
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
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
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 :
l[i:]
sera la liste $[l_i, \dots, l_{n-1}]$l[:i]
sera la liste $[l_0, \dots, l_{i-1}]$l[i:j]
sera la liste $[l_i, \dots, l_{j-1}]$
que donne l[::3]
ou l[1::5]
pour la liste [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]
?
solution
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 :
append
ajoute un élément à la fin d'une liste. Par exemplel.append(3)
ajoute l'entier 3 à la fin d'une liste (sil
valait[1, 4]
avant, elle vaudra[1, 4, 3]
après)insert
ajoute un élément à un index donné d la liste d'une liste. Par exemplel.insert(1, "X")
insère"X"
à l'indice 1 (sil
valait[1, 4]
avant, elle vaudra[1, "X", 4]
après)del
supprime l'élément à l'indice de la liste. Par exempledel l[0]
supprime l'élément d'indice 0 dune liste (sil
valait[1, 4]
avant, elle vaudra[4]
après)
Attention à remove
, extend
ou pop
qui ne font pas ce qu'on croit qu'elle font.
Que font-elles ?
solution
solution
La réponse se trouve dans la documentation :
remove
supprime le premier élément trouvé, pas tousextend
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 :
split
est une méthode des chaînes de caractères qui produit des chainesjoin(liste)
est une méthode des chaînes de caractères qui produit une chaîne à partir d'une liste de chaines de caractère passé en paramètre
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.
- afficher cette liste à l'écran
- triez cette liste puis affichez là à nouveau
- retournez la liste obtenue en 2 puis affichez là à nouveau
solution
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
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 :
- l'opération
+
désigne la concaténation entre deux listes - l'opération
*
par en entier $i$ recopie la liste (ses éléments) $i$ fois.
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 :
[1, 4, "douze"] + 42
produit une erreur puisque42
est un entier et pas une liste.3 * [1, 4, "douze"]
fonctionne également
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
solution
str(m27)[:10]
Quels sont les 10 derniers chiffres de m27
?
solution
solution
str(m27)[-10:]
Est-ce que m27
est un palindrome ?
solution
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
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)