08 - Listes

Définition

Une liste est une structure de données qui permet de stocker plusieurs valeurs dans une seule variable, les unes à la suite des autres. Pensez à une liste de courses, une liste de notes ou un classement sportif : c’est le même principe en Python.

Une liste en Python possède les propriétés suivantes :

  • elle peut contenir un nombre quelconque d’éléments (y compris zéro) ;
  • les éléments sont ordonnés : chacun a une position (un indice) ;
  • les éléments peuvent être de types différents (entiers, chaînes, etc.) ;
  • elle est modifiable (on peut ajouter, supprimer ou remplacer des éléments).

Création d’une liste

On crée une liste en plaçant les éléments entre crochets [], séparés par des virgules :

# Une liste de prénoms
prenoms = ["Alice", "Bob", "Charlie", "Diana"]

# Une liste de notes
notes = [15, 12, 18, 14, 16]

# Une liste vide
panier = []

print(prenoms)
print(notes)
['Alice', 'Bob', 'Charlie', 'Diana']
[15, 12, 18, 14, 16]

On peut aussi créer une liste à partir d’une autre séquence avec la fonction list() :

lettres = list("Python")
print(lettres)
['P', 'y', 't', 'h', 'o', 'n']

Accès aux éléments par indice

Chaque élément est repéré par son indice, c’est-à-dire sa position dans la liste. Le premier élément est à l’indice 0, le deuxième à l’indice 1, et ainsi de suite.

prenoms = ["Alice", "Bob", "Charlie", "Diana"]

print(prenoms[0])   # premier élément
print(prenoms[2])   # troisième élément
print(prenoms[-1])  # dernier élément
Alice
Charlie
Diana

Les indices négatifs permettent de compter à partir de la fin : -1 pour le dernier, -2 pour l’avant-dernier, etc.

Attention : un indice hors limites provoque une erreur.

prenoms = ["Alice", "Bob", "Charlie", "Diana"]
print(prenoms[10])  # IndexError: list index out of range

Longueur d’une liste

La fonction len() renvoie le nombre d’éléments d’une liste :

notes = [15, 12, 18, 14, 16]
print(len(notes))
5

Les indices valides vont donc de 0 à len(L) - 1.

Modifier un élément

Les listes sont mutables : on peut modifier un élément en le réaffectant par son indice.

notes = [15, 12, 18, 14, 16]
print(notes)

notes[1] = 14  # on corrige la deuxième note
print(notes)
[15, 12, 18, 14, 16]
[15, 14, 18, 14, 16]

Parcourir une liste

Parcours par élément (méthode usuelle)

fruits = ["pomme", "banane", "cerise", "mangue"]

for fruit in fruits:
    print(f"J'aime les {fruit}s")
J'aime les pommes
J'aime les bananes
J'aime les cerises
J'aime les mangues

Parcours par indice

fruits = ["pomme", "banane", "cerise", "mangue"]

for i in range(len(fruits)):
    print(f"Fruit n°{i} : {fruits[i]}")
Fruit n°0 : pomme
Fruit n°1 : banane
Fruit n°2 : cerise
Fruit n°3 : mangue

Parcours avec enumerate()

La fonction enumerate() fournit à la fois l’indice et l’élément :

fruits = ["pomme", "banane", "cerise", "mangue"]

for i, fruit in enumerate(fruits):
    print(f"Fruit n°{i} : {fruit}")

Méthodes principales

Ajouter des éléments

couleurs = ["rouge", "vert"]

couleurs.append("bleu")       # ajoute à la fin
print(couleurs)

couleurs.insert(1, "jaune")   # insère à l'indice 1
print(couleurs)
['rouge', 'vert', 'bleu']
['rouge', 'jaune', 'vert', 'bleu']

Supprimer des éléments

couleurs = ["rouge", "jaune", "vert", "bleu"]

couleurs.remove("jaune")  # supprime par valeur
print(couleurs)

dernier = couleurs.pop()   # retire et renvoie le dernier
print(dernier)
print(couleurs)
['rouge', 'vert', 'bleu']
bleu
['rouge', 'vert']

Autres méthodes utiles

MéthodeDescriptionExemple
L.append(x)Ajoute x à la finnotes.append(17)
L.insert(i, x)Insère x à l’indice inotes.insert(0, 20)
L.pop()Retire et renvoie le dernier élémentnotes.pop()
L.remove(x)Retire la première occurrence de xnotes.remove(12)
L.sort()Trie la liste en placenotes.sort()
L.reverse()Inverse l’ordre des élémentsnotes.reverse()
L.index(x)Renvoie l’indice de la première occurrence de xnotes.index(18)
L.count(x)Compte le nombre d’occurrences de xnotes.count(14)

Important : append(), sort(), reverse() et remove() modifient la liste en place et ne renvoient rien (None). Il ne faut donc pas écrire L = L.sort().

Opérations sur les listes

# Concaténation avec +
a = [1, 2, 3]
b = [4, 5]
print(a + b)

# Répétition avec *
print([0] * 5)

# Test d'appartenance avec in
print(3 in a)
print(7 in a)
[1, 2, 3, 4, 5]
[0, 0, 0, 0, 0]
True
False

Fonctions utiles sur les listes

notes = [15, 12, 18, 14, 16]

print(len(notes))    # nombre d'éléments : 5
print(min(notes))    # minimum : 12
print(max(notes))    # maximum : 18
print(sum(notes))    # somme : 75
print(sum(notes) / len(notes))  # moyenne : 15.0

Attention : copie et référence

En Python, une liste est un objet mutable. Quand on écrit b = a, on ne crée pas une copie de la liste : on crée un second nom qui désigne la même liste en mémoire. Toute modification via b affecte aussi a, et inversement.

a = [1, 2, 3]
b = a          # b et a désignent la même liste

b[0] = 99
print(a)
print(b)
[99, 2, 3]
[99, 2, 3]

On dit que a et b sont des alias : deux noms pour le même objet. C’est un piège fréquent.

Pour créer une vraie copie indépendante, on utilise l’une des méthodes suivantes :

a = [1, 2, 3]

b = a.copy()      # méthode 1 : la méthode copy()
c = list(a)       # méthode 2 : le constructeur list()
d = a[:]          # méthode 3 : le slicing complet

b[0] = 99
print(a)   # a n'est pas modifié
print(b)   # seul b est modifié
[1, 2, 3]
[99, 2, 3]

À retenir : = crée un alias, pas une copie. Pour copier une liste, il faut utiliser copy(), list() ou [:].

Effets de bord : listes et fonctions

Quand on passe une liste à une fonction, la fonction reçoit un alias de la liste, pas une copie. Si elle modifie la liste (ajout, suppression, affectation L[i] = ...), la modification est visible par l’appelant après l’appel : on dit que la fonction a un effet de bord.

def ajouter_zero(L):
    L.append(0)

notes = [12, 15]
ajouter_zero(notes)
print(notes)
[12, 15, 0]

Au contraire, réaffecter le paramètre ne touche pas la liste de l’appelant : L = [] fait pointer le nom local L vers une nouvelle liste, et notes continue de désigner l’ancienne.

def vider(L):
    L = []

notes = [12, 15]
vider(notes)
print(notes)
[12, 15]

Deux styles de fonctions existent, et il faut choisir l’un ou l’autre et l’indiquer dans la docstring :

  • une fonction qui modifie en place la liste reçue et ne renvoie rien, comme L.sort() ou L.append() ;
  • une fonction sans effet de bord qui renvoie une nouvelle liste et laisse son argument intact, comme sorted(L).
def doubler_en_place(L):
    """Double chaque élément de L. Modifie L, ne renvoie rien."""
    for i in range(len(L)):
        L[i] = 2 * L[i]

def doubler(L):
    """Renvoie une nouvelle liste dont les éléments sont les doubles de ceux de L. L n'est pas modifiée."""
    resultat = []
    for x in L:
        resultat.append(2 * x)
    return resultat

Les effets de bord sont une source classique de bugs : une liste modifiée « à distance » par une fonction appelée ailleurs dans le programme. Quand une fonction doit travailler sur une copie, on la crée explicitement avec L[:] ou list(L).

Construction par compréhension

Prérequis : cette section utilise la boucle for, présentée dans le chapitre 05 (Boucles). Si vous n’avez pas encore vu les boucles, vous pouvez revenir à cette section plus tard.

La compréhension de liste permet de créer une liste à partir d’une expression et d’un itérable, en une seule ligne. La syntaxe est la suivante : [expression for variable in itérable].

# Les carrés des nombres de 0 à 9
carres = [x ** 2 for x in range(10)]
print(carres)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

On peut ajouter une condition de filtrage avec if :

# Les nombres pairs entre 0 et 19
pairs = [x for x in range(20) if x % 2 == 0]
print(pairs)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Autre exemple : extraire les initiales d’une liste de prénoms.

prenoms = ["Alice", "Bob", "Charlie", "Diana"]
initiales = [p[0] for p in prenoms]
print(initiales)
['A', 'B', 'C', 'D']

Résumé

ConceptSyntaxeExemple
Création[e1, e2, ...][15, 12, 18]
AccèsL[i]notes[0]15
Longueurlen(L)len(notes)3
AjoutL.append(x)notes.append(17)
Parcoursfor x in Lfor n in notes
CopieL.copy() ou L[:]b = a.copy()
Compréhension[expr for x in L][x**2 for x in range(5)]
Appartenancex in L15 in notesTrue