14. Listes : collections ordonnées d’éléments
Jusqu’à présent dans ce livre, nous avons travaillé avec des éléments de données individuels : des nombres simples, des chaînes, et des valeurs booléennes. Mais les programmes réels ont souvent besoin de travailler avec des collections d’éléments liés — une liste de noms d’élèves, une série de relevés de température, une collection de prix de produits, ou une séquence de commandes utilisateur. La liste de Python est l’outil fondamental pour stocker et manipuler des collections ordonnées de données.
Une liste est une séquence qui peut contenir plusieurs éléments dans un ordre spécifique. Contrairement aux chaînes (qui ne peuvent contenir que des caractères), les listes peuvent contenir n’importe quel type de données : nombres, chaînes, booléens, ou même d’autres listes. Les listes sont aussi mutables, ce qui signifie que vous pouvez changer leur contenu après leur création — ajouter des éléments, en retirer, ou modifier ceux qui existent.
Dans ce chapitre, nous allons explorer comment créer des listes, accéder à leurs éléments, les modifier, et les utiliser pour résoudre des problèmes de programmation pratiques. À la fin, vous comprendrez pourquoi les listes sont l’une des structures de données les plus puissantes et les plus fréquemment utilisées en Python.
14.1) Créer des listes et accéder aux éléments
14.1.1) Créer des listes avec des crochets
La manière la plus courante de créer une liste consiste à placer des éléments entre crochets [], avec des éléments séparés par des virgules. Voici un exemple simple :
# Une liste de noms d’élèves
students = ["Alice", "Bob", "Charlie", "Diana"]
print(students) # Output: ['Alice', 'Bob', 'Charlie', 'Diana']Remarquez comment Python affiche la liste : il montre les crochets et met des guillemets autour de chaque chaîne. C’est la représentation de la liste — la manière dont Python vous montre ce qu’il y a dedans.
Les listes peuvent contenir n’importe quel type de données. Voici une liste de notes de test :
# Une liste de notes entières
scores = [85, 92, 78, 95, 88]
print(scores) # Output: [85, 92, 78, 95, 88]Vous pouvez même mélanger différents types dans la même liste, bien que ce soit moins courant en pratique :
# Une liste de types mixtes (moins courant mais valide)
mixed_data = ["Alice", 25, True, 3.14]
print(mixed_data) # Output: ['Alice', 25, True, 3.14]Une liste vide ne contient aucun élément et se crée avec seulement les crochets :
# Une liste vide
empty = []
print(empty) # Output: []
print(len(empty)) # Output: 0La fonction len(), que nous avons utilisée avec les chaînes, fonctionne aussi avec les listes — elle renvoie le nombre d’éléments dans la liste.
14.1.2) Comprendre l’ordre des listes et les positions
Les listes conservent l’ordre dans lequel vous ajoutez des éléments. Le premier élément que vous mettez reste le premier, le second reste le second, et ainsi de suite. Cet ordre est crucial parce qu’il vous permet d’accéder à des éléments spécifiques par leur position (également appelée leur index).
Python utilise un indexage à partir de zéro : le premier élément est à la position 0, le second à la position 1, et ainsi de suite. Cela peut sembler inhabituel au début, mais c’est une convention utilisée par de nombreux langages de programmation.
Voyons comment cela fonctionne en pratique :
students = ["Alice", "Bob", "Charlie", "Diana"]
# Accéder au premier élève (index 0)
first_student = students[0]
print(first_student) # Output: Alice
# Accéder au troisième élève (index 2)
third_student = students[2]
print(third_student) # Output: CharlieRemarquez que pour obtenir le troisième élève, nous utilisons l’index 2, et non 3. C’est parce que le comptage commence à 0.
14.1.3) Accéder aux éléments avec des indices positifs
Pour accéder à un élément de liste, écrivez le nom de la liste suivi de l’index entre crochets : list_name[index]. L’index doit être un entier dans la plage valide (0 à len(list) - 1).
Voici un exemple pratique avec des prix de produits :
# Prix des produits en dollars
prices = [19.99, 24.50, 15.75, 32.00, 8.99]
# Accéder à des prix spécifiques
first_price = prices[0]
last_index = len(prices) - 1 # Calculer le dernier index valide
last_price = prices[last_index]
print(f"First product costs: ${first_price}") # Output: First product costs: $19.99
print(f"Last product costs: ${last_price}") # Output: Last product costs: $8.99Pourquoi utilisons-nous len(prices) - 1 pour le dernier index ? Parce que si une liste a 5 éléments, les indices sont 0, 1, 2, 3, 4 — le dernier index valide est toujours inférieur de 1 à la longueur.
Vous pouvez aussi utiliser des indices dans des expressions et des calculs :
scores = [85, 92, 78, 95, 88]
# Calculer la moyenne des trois premières notes
first_three_average = (scores[0] + scores[1] + scores[2]) / 3
print(f"Average of first three: {first_three_average}") # Output: Average of first three: 85.014.1.4) Indices négatifs : compter depuis la fin
Python fournit une fonctionnalité pratique : les indices négatifs vous permettent d’accéder aux éléments depuis la fin de la liste. L’index -1 correspond au dernier élément, -2 à l’avant-dernier, et ainsi de suite.
students = ["Alice", "Bob", "Charlie", "Diana"]
# Accéder depuis la fin
last_student = students[-1]
second_to_last = students[-2]
print(last_student) # Output: Diana
print(second_to_last) # Output: CharlieC’est particulièrement utile lorsque vous voulez le dernier élément mais ne voulez pas calculer len(list) - 1 :
prices = [19.99, 24.50, 15.75, 32.00, 8.99]
# Ces deux approches sont équivalentes
last_price_method1 = prices[len(prices) - 1]
last_price_method2 = prices[-1]
print(last_price_method1) # Output: 8.99
print(last_price_method2) # Output: 8.99Voici comment les indices positifs et négatifs correspondent aux mêmes éléments :
14.1.5) Que se passe-t-il avec des indices invalides
Si vous essayez d’accéder à un index qui n’existe pas, Python lève une IndexError :
students = ["Alice", "Bob", "Charlie"]
# AVERTISSEMENT : cette liste a les indices 0, 1, 2 (ou -3, -2, -1) - pour démonstration uniquement
# Essayer d’accéder à l’index 3 provoque une erreur
# PROBLÈME : l’index 3 n’existe pas dans une liste de 3 éléments
# print(students[3]) # IndexError: list index out of rangeCette erreur est la façon dont Python vous indique que vous avez demandé un élément qui n’est pas présent.
14.2) Indexation et slicing des listes
14.2.1) Comprendre les bases du slicing des listes
Tout comme nous pouvons découper les chaînes (comme nous l’avons appris au chapitre 5), nous pouvons découper les listes pour en extraire des portions. Un slice crée une nouvelle liste contenant un sous-ensemble des éléments de la liste d’origine. La syntaxe est list[start:stop], où start est l’index où le slice commence (inclus) et stop est là où il se termine (exclu).
numbers = [10, 20, 30, 40, 50, 60, 70]
# Obtenir les éléments de l’index 1 jusqu’à (sans inclure) l’index 4
subset = numbers[1:4]
print(subset) # Output: [20, 30, 40]Le slice [1:4] inclut les indices 1, 2 et 3, mais s’arrête avant l’index 4. Cette règle « stop est exclusif » est la même qu’avec le slicing des chaînes.
Voyons un exemple pratique avec des noms d’élèves :
students = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank"]
# Obtenir les trois premiers élèves
first_three = students[0:3]
print(first_three) # Output: ['Alice', 'Bob', 'Charlie']
# Obtenir les élèves de l’index 2 à 4
middle_group = students[2:5]
print(middle_group) # Output: ['Charlie', 'Diana', 'Eve']14.2.2) Omettre start ou stop dans les slices
Vous pouvez omettre l’index de début pour découper depuis le début, ou omettre l’index de fin pour découper jusqu’à la fin :
scores = [85, 92, 78, 95, 88, 91, 87]
# Depuis le début jusqu’à l’index 3
first_few = scores[:3]
print(first_few) # Output: [85, 92, 78]
# De l’index 4 jusqu’à la fin
last_few = scores[4:]
print(last_few) # Output: [88, 91, 87]
# La liste entière (du début à la fin)
all_scores = scores[:]
print(all_scores) # Output: [85, 92, 78, 95, 88, 91, 87]Le slice [:] crée une copie de la liste entière. C’est utile lorsque vous voulez travailler sur un doublon sans modifier l’original — nous explorerons cela davantage dans la section 14.6.
14.2.3) Utiliser des indices négatifs dans les slices
Les indices négatifs fonctionnent dans les slices de la même manière que pour l’accès à un seul élément. C’est particulièrement utile pour obtenir des éléments depuis la fin :
students = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank"]
# Obtenir les trois derniers élèves
last_three = students[-3:]
print(last_three) # Output: ['Diana', 'Eve', 'Frank']
# Obtenir tous sauf les deux derniers élèves
all_but_last_two = students[:-2]
print(all_but_last_two) # Output: ['Alice', 'Bob', 'Charlie', 'Diana']
# Obtenir du troisième depuis la fin jusqu’au deuxième depuis la fin
middle_from_end = students[-3:-1]
print(middle_from_end) # Output: ['Diana', 'Eve']14.2.4) Slicing avec une valeur de pas
Vous pouvez ajouter un troisième paramètre pour contrôler le pas (combien d’indices sauter entre les éléments). La syntaxe complète est list[start:stop:step] :
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# Un nombre sur deux à partir de l’index 0
evens = numbers[0:10:2]
print(evens) # Output: [0, 2, 4, 6, 8]
# Un nombre sur trois à partir de l’index 1
every_third = numbers[1:10:3]
print(every_third) # Output: [1, 4, 7]Vous pouvez aussi utiliser un pas négatif pour inverser la liste :
numbers = [1, 2, 3, 4, 5]
# Inverser la liste
reversed_numbers = numbers[::-1]
print(reversed_numbers) # Output: [5, 4, 3, 2, 1]Le slice [::-1] signifie « commencer à la fin, aller au début, en reculant de 1 ». C’est un idiome Python courant pour inverser des séquences.
14.2.5) Les slices ne provoquent jamais d’IndexError
Contrairement à l’accès à un seul élément, le slicing est très tolérant. Si vous spécifiez des indices hors de la plage de la liste, Python les ajuste simplement pour qu’ils correspondent :
numbers = [10, 20, 30, 40, 50]
# Demander plus que ce qui existe
extended_slice = numbers[2:100]
print(extended_slice) # Output: [30, 40, 50]
# Commencer au-delà de la fin
empty_slice = numbers[10:20]
print(empty_slice) # Output: []Ce comportement est utile car cela signifie que vous n’avez pas à vous soucier de limites exactes lors d’un slicing — Python gère les cas limites avec élégance.
14.3) Modifier les listes et méthodes courantes des listes
14.3.1) Les listes sont mutables : changer des éléments
Contrairement aux chaînes, qui sont immuables, les listes sont mutables — vous pouvez changer leur contenu après leur création. Vous pouvez modifier des éléments individuels en affectant de nouvelles valeurs à des indices spécifiques :
# Commencer avec une liste de prix
prices = [19.99, 24.50, 15.75, 32.00]
print(prices) # Output: [19.99, 24.5, 15.75, 32.0]
# Mettre à jour le deuxième prix (index 1)
prices[1] = 22.99
print(prices) # Output: [19.99, 22.99, 15.75, 32.0]
# Mettre à jour le dernier prix avec un indexage négatif
prices[-1] = 29.99
print(prices) # Output: [19.99, 22.99, 15.75, 29.99]Cette mutabilité est puissante — cela signifie que vous pouvez mettre à jour des données sur place sans créer de nouvelles listes. Cependant, cela signifie aussi que vous devez faire attention aux changements involontaires, ce dont nous parlerons dans la section 14.6.
14.3.2) Ajouter des éléments avec append()
La méthode append() ajoute un seul élément à la fin d’une liste. C’est l’une des opérations sur les listes les plus fréquemment utilisées :
# Commencer avec un panier d’achat vide
cart = []
print(cart) # Output: []
# Ajouter des éléments un par un
cart.append("Milk")
print(cart) # Output: ['Milk']
cart.append("Bread")
print(cart) # Output: ['Milk', 'Bread']
cart.append("Eggs")
print(cart) # Output: ['Milk', 'Bread', 'Eggs']Remarquez que append() modifie la liste sur place — elle ne renvoie pas une nouvelle liste. La méthode renvoie None, vous n’avez donc pas besoin d’affecter son résultat :
scores = [85, 92, 78]
result = scores.append(95)
print(scores) # Output: [85, 92, 78, 95]
print(result) # Output: None14.3.3) Insérer des éléments à des positions spécifiques avec insert()
Alors que append() ajoute toujours à la fin, insert() vous permet d’ajouter un élément à n’importe quelle position. La syntaxe est list.insert(index, item) :
students = ["Alice", "Charlie", "Diana"]
print(students) # Output: ['Alice', 'Charlie', 'Diana']
# Insérer "Bob" à l’index 1 (entre Alice et Charlie)
students.insert(1, "Bob")
print(students) # Output: ['Alice', 'Bob', 'Charlie', 'Diana']Lorsque vous insérez à un index, l’élément actuellement à cette position (et tous ceux qui le suivent) se décalent vers la droite :
numbers = [10, 20, 30, 40]
print(numbers) # Output: [10, 20, 30, 40]
# Insérer 25 à l’index 2
numbers.insert(2, 25)
print(numbers) # Output: [10, 20, 25, 30, 40]Vous pouvez insérer au début en utilisant l’index 0 :
priorities = ["Medium", "Low"]
priorities.insert(0, "High")
print(priorities) # Output: ['High', 'Medium', 'Low']Si vous spécifiez un index au-delà de la longueur de la liste, insert() ajoute simplement l’élément à la fin (comme append()) :
items = [1, 2, 3]
items.insert(100, 4)
print(items) # Output: [1, 2, 3, 4]14.3.4) Supprimer des éléments avec remove()
La méthode remove() supprime la première occurrence d’une valeur spécifique dans la liste :
fruits = ["apple", "banana", "cherry", "banana", "date"]
print(fruits) # Output: ['apple', 'banana', 'cherry', 'banana', 'date']
# Supprimer le premier "banana"
fruits.remove("banana")
print(fruits) # Output: ['apple', 'cherry', 'banana', 'date']Remarquez que seul le premier « banana » a été supprimé — le second est toujours là. Si vous essayez de supprimer une valeur qui n’existe pas, Python lève une ValueError :
numbers = [10, 20, 30]
# AVERTISSEMENT : tentative de suppression d’une valeur inexistante - pour démonstration uniquement
# PROBLÈME : 40 n’est pas dans la liste
# numbers.remove(40) # ValueError: list.remove(x): x not in listPour éviter cette erreur, vous pouvez vérifier si l’élément existe avant de le supprimer :
cart = ["Milk", "Bread", "Eggs"]
item_to_remove = "Butter"
if item_to_remove in cart:
cart.remove(item_to_remove)
print(f"Removed {item_to_remove}")
else:
print(f"{item_to_remove} not in cart")
# Output: Butter not in cart14.3.5) Supprimer et renvoyer des éléments avec pop()
La méthode pop() supprime un élément à un index spécifique et le renvoie. Si vous ne spécifiez pas d’index, elle supprime et renvoie le dernier élément :
scores = [85, 92, 78, 95, 88]
# Supprimer et récupérer la dernière note
last_score = scores.pop()
print(f"Removed: {last_score}") # Output: Removed: 88
print(scores) # Output: [85, 92, 78, 95]
# Supprimer et récupérer la note à l’index 1
second_score = scores.pop(1)
print(f"Removed: {second_score}") # Output: Removed: 92
print(scores) # Output: [85, 78, 95]C’est utile lorsque vous devez traiter des éléments d’une liste un par un :
tasks = ["Write code", "Test code", "Deploy code"]
while len(tasks) > 0:
current_task = tasks.pop(0) # Retirer depuis le début
print(f"Working on: {current_task}")
# Output:
# Working on: Write code
# Working on: Test code
# Working on: Deploy code
print(tasks) # Output: []14.3.6) Étendre des listes avec extend()
La méthode extend() ajoute tous les éléments d’une autre liste (ou de n’importe quel itérable) à la fin de la liste actuelle :
primary_colors = ["red", "blue", "yellow"]
secondary_colors = ["green", "orange", "purple"]
# Ajouter toutes les couleurs secondaires aux couleurs primaires
primary_colors.extend(secondary_colors)
print(primary_colors)
# Output: ['red', 'blue', 'yellow', 'green', 'orange', 'purple']C’est différent de append(), qui ajouterait la liste entière comme un seul élément :
colors1 = ["red", "blue"]
colors2 = ["green", "orange"]
# Utiliser append (ajoute la liste comme un seul élément)
colors1.append(colors2)
print(colors1) # Output: ['red', 'blue', ['green', 'orange']]
# Utiliser extend (ajoute chaque élément individuellement)
colors3 = ["red", "blue"]
colors3.extend(colors2)
print(colors3) # Output: ['red', 'blue', 'green', 'orange']14.3.7) Trier des listes avec sort() et sorted()
Python fournit deux façons de trier des listes. La méthode sort() trie la liste sur place (en modifiant l’original) :
scores = [78, 95, 85, 92, 88]
scores.sort()
print(scores) # Output: [78, 85, 88, 92, 95]Pour trier dans l’ordre décroissant, utilisez le paramètre reverse :
scores = [78, 95, 85, 92, 88]
scores.sort(reverse=True)
print(scores) # Output: [95, 92, 88, 85, 78]La fonction sorted() (que nous explorerons davantage au chapitre 38) crée une nouvelle liste triée sans modifier l’original :
original = [78, 95, 85, 92, 88]
sorted_scores = sorted(original)
print(original) # Output: [78, 95, 85, 92, 88]
print(sorted_scores) # Output: [78, 85, 88, 92, 95]Le tri fonctionne aussi avec des chaînes, en utilisant l’ordre alphabétique :
names = ["Charlie", "Alice", "Diana", "Bob"]
names.sort()
print(names) # Output: ['Alice', 'Bob', 'Charlie', 'Diana']14.3.8) Inverser des listes avec reverse()
La méthode reverse() inverse la liste sur place :
numbers = [1, 2, 3, 4, 5]
numbers.reverse()
print(numbers) # Output: [5, 4, 3, 2, 1]C’est différent d’un tri en ordre inverse — reverse() se contente de retourner l’ordre actuel, quel qu’il soit :
mixed = [3, 1, 4, 1, 5]
mixed.reverse()
print(mixed) # Output: [5, 1, 4, 1, 3]Souvenez-vous que vous pouvez aussi inverser une liste en utilisant le slicing : list[::-1]. La différence est que le slicing crée une nouvelle liste, tandis que reverse() modifie l’original.
14.3.9) Trouver des éléments avec index() et count()
La méthode index() renvoie la position de la première occurrence d’une valeur :
students = ["Alice", "Bob", "Charlie", "Diana", "Bob"]
# Trouver où se trouve "Charlie"
position = students.index("Charlie")
print(f"Charlie is at index {position}") # Output: Charlie is at index 2
# Trouver le premier "Bob"
bob_position = students.index("Bob")
print(f"Bob is at index {bob_position}") # Output: Bob is at index 1Si la valeur n’existe pas, index() lève une ValueError :
students = ["Alice", "Bob", "Charlie"]
# AVERTISSEMENT : tentative de recherche d’une valeur inexistante - pour démonstration uniquement
# PROBLÈME : 'Eve' n’est pas dans la liste
# position = students.index("Eve") # ValueError: 'Eve' is not in listLa méthode count() renvoie le nombre de fois où une valeur apparaît :
numbers = [1, 2, 3, 2, 4, 2, 5]
twos = numbers.count(2)
print(f"The number 2 appears {twos} times") # Output: The number 2 appears 3 times
# count peut renvoyer 0 si l’élément n’existe pas
sixes = numbers.count(6)
print(f"The number 6 appears {sixes} times") # Output: The number 6 appears 0 times14.3.10) Vider tous les éléments avec clear()
La méthode clear() supprime tous les éléments d’une liste, en la laissant vide :
cart = ["Milk", "Bread", "Eggs", "Butter"]
print(cart) # Output: ['Milk', 'Bread', 'Eggs', 'Butter']
cart.clear()
print(cart) # Output: []
print(len(cart)) # Output: 0C’est équivalent à affecter une liste vide, mais clear() est plus explicite quant à l’intention.
14.4) Supprimer des éléments de liste avec del
14.4.1) Utiliser del pour supprimer des éléments par index
L’instruction del peut supprimer des éléments de liste à des indices spécifiques :
students = ["Alice", "Bob", "Charlie", "Diana", "Eve"]
print(students) # Output: ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve']
# Supprimer l’élément à l’index 2
del students[2]
print(students) # Output: ['Alice', 'Bob', 'Diana', 'Eve']Contrairement à pop(), del ne renvoie pas la valeur supprimée — elle la supprime simplement. C’est utile lorsque vous voulez retirer un élément mais n’avez pas besoin de l’utiliser :
scores = [85, 92, 78, 95, 88]
# Supprimer la note la plus faible (à l’index 2)
del scores[2]
print(scores) # Output: [85, 92, 95, 88]Vous pouvez aussi utiliser des indices négatifs avec del :
tasks = ["Task 1", "Task 2", "Task 3", "Task 4"]
# Supprimer la dernière tâche
del tasks[-1]
print(tasks) # Output: ['Task 1', 'Task 2', 'Task 3']14.4.2) Supprimer des slices avec del
L’instruction del peut retirer des slices entiers en une seule fois :
numbers = [10, 20, 30, 40, 50, 60, 70]
print(numbers) # Output: [10, 20, 30, 40, 50, 60, 70]
# Supprimer les éléments de l’index 2 à 4 (indices 2, 3, 4)
del numbers[2:5]
print(numbers) # Output: [10, 20, 60, 70]C’est particulièrement utile pour supprimer des plages d’éléments :
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Retirer les trois premiers éléments
del data[:3]
print(data) # Output: [4, 5, 6, 7, 8, 9, 10]
# Retirer les deux derniers éléments
del data[-2:]
print(data) # Output: [4, 5, 6, 7, 8]Vous pouvez même supprimer un élément sur deux en utilisant le slicing avec pas :
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# Supprimer un élément sur deux
del numbers[::2]
print(numbers) # Output: [1, 3, 5, 7, 9]14.4.3) Comparer del, remove(), et pop()
Clarifions quand utiliser chaque méthode de suppression :
# Liste exemple pour comparaison
items = ["apple", "banana", "cherry", "date", "elderberry"]
# Utiliser remove() quand vous connaissez la VALEUR à supprimer
items_copy1 = items.copy()
items_copy1.remove("cherry") # Supprime le premier "cherry"
print(items_copy1) # Output: ['apple', 'banana', 'date', 'elderberry']
# Utiliser pop() quand vous connaissez l’INDEX et avez besoin de la valeur
items_copy2 = items.copy()
removed_item = items_copy2.pop(2) # Supprime et renvoie l’élément à l’index 2
print(f"Removed: {removed_item}") # Output: Removed: cherry
print(items_copy2) # Output: ['apple', 'banana', 'date', 'elderberry']
# Utiliser del quand vous connaissez l’INDEX mais n’avez pas besoin de la valeur
items_copy3 = items.copy()
del items_copy3[2] # Retire simplement l’élément à l’index 2
print(items_copy3) # Output: ['apple', 'banana', 'date', 'elderberry']14.5) Parcourir des listes avec des boucles for
14.5.1) Parcours de base d’une liste
L’une des opérations les plus courantes avec les listes consiste à traiter chaque élément dans l’ordre. La boucle for (que nous avons apprise au chapitre 12) est parfaite pour cela :
students = ["Alice", "Bob", "Charlie", "Diana"]
# Traiter chaque élève
for student in students:
print(f"Hello, {student}!")
# Output:
# Hello, Alice!
# Hello, Bob!
# Hello, Charlie!
# Hello, Diana!La variable de boucle (student dans ce cas) prend chaque valeur de la liste, une à une, dans l’ordre. Vous pouvez donner à cette variable n’importe quel nom significatif :
scores = [85, 92, 78, 95, 88]
# Calculer et afficher la note (grade) de chaque score
for score in scores:
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
else:
grade = "C"
print(f"Score {score} is a {grade}")
# Output:
# Score 85 is a B
# Score 92 is a A
# Score 78 is a C
# Score 95 is a A
# Score 88 is a B14.5.2) Traiter des éléments correspondants de plusieurs listes
Parfois, vous devez travailler avec des données liées stockées dans des listes séparées. Nous apprendrons la fonction zip() en détail au chapitre 38, mais voici un aperçu rapide de la façon dont elle peut aider à traiter des éléments correspondants :
# Nous apprendrons zip() au chapitre 38, mais pour l’instant voici un exemple simple
students = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
# Traiter les paires correspondantes
for student, score in zip(students, scores):
print(f"{student} scored {score}")
# Output:
# Alice scored 85
# Bob scored 92
# Charlie scored 78La fonction zip() associe des éléments de plusieurs listes, ce qui est utile lorsque vous avez des données liées dans des listes séparées. Nous explorerons cela ainsi que d’autres outils d’itération en profondeur au chapitre 38.
14.6) Copier des listes et éviter les références partagées
14.6.1) Comprendre les références de liste
Lorsque vous affectez une liste à une variable, Python ne crée pas une copie de la liste — il crée une référence vers le même objet liste en mémoire. Cela signifie que plusieurs variables peuvent référencer la même liste :
original = [1, 2, 3]
reference = original # Les deux variables pointent vers la MÊME liste
# Modifier via une variable affecte l’autre
reference.append(4)
print(original) # Output: [1, 2, 3, 4]
print(reference) # Output: [1, 2, 3, 4]Ce comportement peut surprendre si vous vous attendez à ce que reference soit une copie indépendante. Voyons pourquoi c’est important :
# Scénario : vous voulez suivre les changements dans un panier d’achat
cart = ["Milk", "Bread"]
backup = cart # Tenter de sauvegarder l’état original
# Ajouter plus d’articles
cart.append("Eggs")
cart.append("Butter")
# Vérifier la "sauvegarde"
print(backup) # Output: ['Milk', 'Bread', 'Eggs', 'Butter']La sauvegarde a changé aussi ! C’est parce que backup et cart sont deux noms pour le même objet liste.
14.6.2) Créer des copies indépendantes avec le slicing
Pour créer une véritable copie indépendante, utilisez le slicing avec [:] :
original = [1, 2, 3]
copy = original[:] # Crée une NOUVELLE liste avec le même contenu
# Modifier la copie n’affecte pas l’original
copy.append(4)
print(original) # Output: [1, 2, 3]
print(copy) # Output: [1, 2, 3, 4]Maintenant, corrigeons notre exemple de panier d’achat :
cart = ["Milk", "Bread"]
backup = cart[:] # Créer une copie indépendante
# Ajouter plus d’articles au panier
cart.append("Eggs")
cart.append("Butter")
# La sauvegarde reste inchangée
print(cart) # Output: ['Milk', 'Bread', 'Eggs', 'Butter']
print(backup) # Output: ['Milk', 'Bread']14.6.3) Créer des copies avec la méthode copy()
Les listes ont aussi une méthode copy() qui fait la même chose que [:] :
original = [10, 20, 30]
copy = original.copy()
copy.append(40)
print(original) # Output: [10, 20, 30]
print(copy) # Output: [10, 20, 30, 40]À la fois [:] et copy() créent des copies superficielles, dont nous discuterons ensuite.
14.6.4) La limitation de la copie superficielle
À la fois [:] et copy() créent des copies superficielles. Cela signifie qu’elles copient la structure de la liste, mais si la liste contient d’autres objets mutables (comme d’autres listes), ces objets internes sont toujours partagés :
# Une liste contenant des listes
original = [[1, 2], [3, 4], [5, 6]]
copy = original[:]
# Modifier la structure de la liste externe est indépendant
copy.append([7, 8])
print(original) # Output: [[1, 2], [3, 4], [5, 6]]
print(copy) # Output: [[1, 2], [3, 4], [5, 6], [7, 8]]
# Mais modifier une liste interne affecte les deux !
copy[0].append(99)
print(original) # Output: [[1, 2, 99], [3, 4], [5, 6]]
print(copy) # Output: [[1, 2, 99], [3, 4], [5, 6], [7, 8]]Pourquoi cela se produit-il ? Parce que la copie superficielle crée une nouvelle liste externe, mais les listes internes restent des références partagées :
Pour des structures imbriquées, vous auriez besoin d’une copie profonde, que nous apprendrons lorsque nous explorerons le module copy dans des chapitres ultérieurs. Pour l’instant, sachez que les copies superficielles fonctionnent parfaitement pour des listes d’éléments immuables (nombres, chaînes, tuples), mais demandent de la prudence avec des structures mutables imbriquées.
14.6.5) Quand les références partagées sont utiles
Parfois, vous voulez que plusieurs variables fassent référence à la même liste. C’est utile lorsque vous devez modifier une liste depuis différentes parties de votre code :
# Une fonction qui modifie une liste sur place
def add_bonus_points(scores, bonus):
for i in range(len(scores)):
scores[i] = scores[i] + bonus
# La liste d’origine est modifiée
student_scores = [85, 92, 78]
add_bonus_points(student_scores, 5)
print(student_scores) # Output: [90, 97, 83]Cela fonctionne parce que la fonction reçoit une référence vers la liste d’origine, pas une copie. Nous explorerons cela davantage lorsque nous étudierons les fonctions en détail dans la Partie V.
14.7) Utiliser enumerate() lors du parcours des listes
14.7.1) Le besoin d’avoir à la fois l’index et la valeur
Parfois, lors de l’itération sur une liste, vous avez besoin à la fois de l’index et de la valeur. Une approche consiste à utiliser range(len(list)) :
students = ["Alice", "Bob", "Charlie", "Diana"]
for i in range(len(students)):
print(f"Student {i}: {students[i]}")
# Output:
# Student 0: Alice
# Student 1: Bob
# Student 2: Charlie
# Student 3: DianaCela fonctionne, mais ce n’est pas très élégant. Vous devez utiliser students[i] pour accéder à chaque valeur, ce qui est moins lisible que de parcourir directement les valeurs.
14.7.2) Utiliser enumerate() pour un code plus propre
La fonction enumerate() fournit une meilleure solution. Elle renvoie à la fois l’index et la valeur pour chaque élément :
students = ["Alice", "Bob", "Charlie", "Diana"]
for index, student in enumerate(students):
print(f"Student {index}: {student}")
# Output:
# Student 0: Alice
# Student 1: Bob
# Student 2: Charlie
# Student 3: DianaLa syntaxe for index, value in enumerate(list) dépaquette chaque paire produite par enumerate(). C’est bien plus lisible que d’utiliser range(len()).
14.7.3) Démarrer enumerate() à un nombre différent
Par défaut, enumerate() commence à compter à 0. Vous pouvez spécifier un autre nombre de départ avec le paramètre start :
students = ["Alice", "Bob", "Charlie", "Diana"]
# Commencer à compter à 1 au lieu de 0
for position, student in enumerate(students, start=1):
print(f"Position {position}: {student}")
# Output:
# Position 1: Alice
# Position 2: Bob
# Position 3: Charlie
# Position 4: DianaC’est utile lorsque vous voulez afficher une numérotation adaptée aux humains (commençant à 1) plutôt qu’un indexage adapté aux programmeurs (commençant à 0).
Exemples pratiques avec enumerate()
Voici un exemple pratique qui affiche un menu numéroté :
menu_items = ["New Game", "Load Game", "Settings", "Quit"]
print("Main Menu:")
for number, item in enumerate(menu_items, start=1):
print(f"{number}. {item}")
# Output:
# Main Menu:
# 1. New Game
# 2. Load Game
# 3. Settings
# 4. Quit14.7.4) Modifier des listes avec enumerate()
Vous pouvez utiliser enumerate() lorsque vous devez modifier des éléments de liste en fonction de leur position :
# Ajouter un bonus basé sur la position aux scores
scores = [85, 92, 78, 95, 88]
for index, score in enumerate(scores):
# Le premier élève reçoit 5 points de bonus, le deuxième reçoit 4, etc.
bonus = 5 - index
if bonus > 0:
scores[index] = score + bonus
print(scores) # Output: [90, 96, 81, 97, 89]14.8) Modèles courants avec les listes : recherche, filtrage et agrégation de données
14.8.1) Rechercher des éléments dans des listes
L’une des tâches les plus courantes consiste à vérifier si une liste contient un élément spécifique. L’opérateur in (que nous avons appris au chapitre 7) rend cela simple :
students = ["Alice", "Bob", "Charlie", "Diana"]
# Vérifier si un élève est dans la liste
if "Charlie" in students:
print("Charlie is enrolled") # Output: Charlie is enrolled
if "Eve" not in students:
print("Eve is not enrolled") # Output: Eve is not enrolledPour trouver la position d’un élément, utilisez la méthode index() (couverte dans la section 14.3.9), mais rappelez-vous de vérifier d’abord que l’élément existe :
scores = [85, 92, 78, 95, 88]
target_score = 95
if target_score in scores:
position = scores.index(target_score)
print(f"Score {target_score} found at index {position}")
# Output: Score 95 found at index 3
else:
print(f"Score {target_score} not found")14.8.2) Trouver les valeurs maximale et minimale
Les fonctions intégrées de Python max() et min() fonctionnent avec les listes :
scores = [85, 92, 78, 95, 88, 91, 76]
highest_score = max(scores)
lowest_score = min(scores)
print(f"Highest score: {highest_score}") # Output: Highest score: 95
print(f"Lowest score: {lowest_score}") # Output: Lowest score: 7614.8.3) Calculer des agrégats : somme, moyenne et nombre
Calculer des totaux et des moyennes est une opération fondamentale sur les listes :
scores = [85, 92, 78, 95, 88, 91, 76, 89]
# Calculer le total et la moyenne
total = sum(scores)
count = len(scores)
average = total / count
print(f"Total: {total}") # Output: Total: 694
print(f"Count: {count}") # Output: Count: 8
print(f"Average: {average:.2f}") # Output: Average: 86.75Voici un exemple pratique qui calcule le total d’un panier :
cart_items = ["Milk", "Bread", "Eggs", "Butter", "Cheese"]
prices = [3.99, 2.49, 4.99, 5.49, 6.99]
# Calculer le coût total
total_cost = sum(prices)
item_count = len(cart_items)
print(f"Items in cart: {item_count}")
print(f"Total cost: ${total_cost:.2f}")
# Output:
# Items in cart: 5
# Total cost: $23.9514.9) Mutabilité des listes et truthiness dans les conditions
14.9.1) Comprendre la mutabilité des listes en pratique
Nous avons vu tout au long de ce chapitre que les listes sont mutables — elles peuvent être modifiées après leur création. Cette mutabilité est ce qui rend les listes si puissantes pour stocker et manipuler des collections de données. Consolidons notre compréhension avec un exemple complet :
# Commencer avec une liste de tâches vide
tasks = []
print(f"Initial tasks: {tasks}") # Output: Initial tasks: []
# Ajouter des tâches
tasks.append("Write code")
tasks.append("Test code")
tasks.append("Deploy code")
print(f"After adding: {tasks}")
# Output: After adding: ['Write code', 'Test code', 'Deploy code']
# Insérer une tâche urgente au début
tasks.insert(0, "Review requirements")
print(f"After inserting: {tasks}")
# Output: After inserting: ['Review requirements', 'Write code', 'Test code', 'Deploy code']
# Terminer et supprimer la première tâche
completed = tasks.pop(0)
print(f"Completed: {completed}") # Output: Completed: Review requirements
print(f"Remaining: {tasks}")
# Output: Remaining: ['Write code', 'Test code', 'Deploy code']
# Modifier une tâche
tasks[1] = "Test code thoroughly"
print(f"After modifying: {tasks}")
# Output: After modifying: ['Write code', 'Test code thoroughly', 'Deploy code']14.9.2) Mutabilité vs immutabilité : listes vs chaînes
Il est important de comprendre la différence entre les listes mutables et les chaînes immuables. Avec les chaînes, les opérations créent de nouvelles chaînes plutôt que de modifier l’originale :
# Les chaînes sont immuables
text = "hello"
text.upper() # Crée une nouvelle chaîne, ne change pas l’original
print(text) # Output: hello (unchanged)
# Pour "changer" une chaîne, vous devez réaffecter
text = text.upper()
print(text) # Output: HELLO
# Les listes sont mutables
numbers = [1, 2, 3]
numbers.append(4) # Modifie la liste sur place
print(numbers) # Output: [1, 2, 3, 4] (changed)Cette différence affecte la façon dont vous travaillez avec ces types :
# Les opérations sur les chaînes nécessitent une réaffectation
name = "alice"
name = name.capitalize() # Il faut réaffecter pour voir le changement
print(name) # Output: Alice
# Les opérations sur les listes modifient sur place
scores = [85, 92, 78]
scores.append(95) # Pas besoin de réaffectation
print(scores) # Output: [85, 92, 78, 95]14.9.3) Utiliser des listes dans des contextes booléens
Les listes ont une valeur de vérité : une liste vide est considérée comme False, et toute liste non vide est considérée comme True. C’est utile dans les instructions conditionnelles :
# Une liste vide est falsy
empty_cart = []
if empty_cart:
print("Cart has items")
else:
print("Cart is empty") # Output: Cart is empty
# Une liste non vide est truthy
cart_with_items = ["Milk", "Bread"]
if cart_with_items:
print("Cart has items") # Output: Cart has itemsCe modèle est couramment utilisé pour vérifier si une liste contient des éléments avant de la traiter :
students = ["Alice", "Bob", "Charlie"]
if students:
print(f"We have {len(students)} students")
for student in students:
print(f" - {student}")
else:
print("No students enrolled")
# Output:
# We have 3 students
# - Alice
# - Bob
# - Charlie14.9.4) Modèle pratique : traiter jusqu’à ce que ce soit vide
La valeur de vérité des listes permet un modèle utile pour traiter des éléments jusqu’à ce qu’une liste soit vide :
# Traiter des tâches jusqu’à ce qu’il n’en reste plus
tasks = ["Task 1", "Task 2", "Task 3"]
while tasks: # Continuer tant que la liste n’est pas vide
current_task = tasks.pop(0)
print(f"Processing: {current_task}")
print("All tasks completed!")
# Output:
# Processing: Task 1
# Processing: Task 2
# Processing: Task 3
# All tasks completed!14.9.5) Vérifier les listes vides : explicite vs implicite
Il y a deux façons de vérifier si une liste est vide :
items = []
# Vérification implicite (Pythonic)
if not items:
print("List is empty") # Output: List is empty
# Vérification explicite (également valide)
if len(items) == 0:
print("List is empty") # Output: List is emptyLa vérification implicite (if not items:) est généralement préférée en Python car elle est plus concise et fonctionne avec tout type de collection. Cependant, les deux approches sont correctes et vous verrez les deux dans du code réel.
14.9.6) Mutabilité et comportement des fonctions
Lorsque vous passez une liste à une fonction (que nous explorerons en détail dans la Partie V), la fonction reçoit une référence vers le même objet liste. Cela signifie que la fonction peut modifier la liste originale :
def add_item(shopping_list, item):
shopping_list.append(item)
print(f"Added {item}")
# La liste originale est modifiée
cart = ["Milk", "Bread"]
print(f"Before: {cart}") # Output: Before: ['Milk', 'Bread']
add_item(cart, "Eggs") # Output: Added Eggs
print(f"After: {cart}") # Output: After: ['Milk', 'Bread', 'Eggs']Ce comportement est différent des types immuables comme les chaînes et les nombres, où la valeur originale ne peut pas être modifiée par une fonction. Comprendre cette distinction est crucial pour écrire des programmes corrects.
Les listes sont l’une des structures de données les plus fondamentales et polyvalentes de Python. Elles fournissent une collection ordonnée et mutable qui peut grandir et rétrécir selon les besoins, ce qui les rend parfaites pour stocker et traiter des séquences de données liées. Vous avez appris à créer des listes, accéder à leurs éléments via l’indexation et le slicing, les modifier avec diverses méthodes, les parcourir efficacement, et comprendre leur nature mutable.
Les modèles que nous avons explorés — recherche, filtrage, agrégation, et transformation de données — constituent la base du travail avec des collections en Python. Au fur et à mesure que vous continuerez à apprendre, vous découvrirez des façons encore plus puissantes de travailler avec des listes, notamment les compréhensions de listes (chapitre 35) et des techniques d’itération avancées (chapitres 36-37). Mais les fondamentaux que vous avez maîtrisés dans ce chapitre vous serviront tout au long de votre parcours de programmation en Python.