Etude des types mutables en pyton et problèmes associés
Dans ce cours, nous allons explorer les types mutables en Python et les problèmes qu'ils peuvent engendrer. Nous verrons pourquoi la mutabilité, bien que pratique, peut conduire à des bugs subtils et comment les éviter grâce à de bonnes pratiques de programmation.
En Python, les types mutables (comme les listes, dictionnaires et ensembles) permettent de modifier leur contenu après création. Cette caractéristique, bien que puissante, peut entraîner des comportements inattendus si mal maîtrisée.
a = [1, 2, 3] b = a # b référence la même liste que a b.append(4) print(a) # [1, 2, 3, 4] → a a aussi été modifié !
def ajouter_element(element, liste=[]): liste.append(element) return liste print(ajouter_element(1)) # [1] print(ajouter_element(2)) # [1, 2] → La liste persiste entre les appels !
nombres = [1, 2, 3, 4] for n in nombres: if n % 2 == 0: nombres.remove(n) # Risque de sauter des éléments print(nombres) # Résultat inattendu possible
Problème | Solution |
---|---|
Copies accidentelles | Utiliser copy() ou list() pour une vraie copie |
Arguments par défaut | Utiliser None comme valeur par défaut |
Modification pendant itération | Itérer sur une copie ou utiliser des compréhensions de liste |
original = [1, 2, 3] copie = original.copy() # ou list(original) copie.append(4) print(original) # [1, 2, 3] → inchangé
def ajouter_element(element, liste=None): if liste is None: liste = [] liste.append(element) return liste
# Solution 1 : Itérer sur une copie for n in nombres.copy(): if n % 2 == 0: nombres.remove(n) # Solution 2 : Compréhension de liste nombres = [n for n in nombres if n % 2 != 0]
La mutabilité en Python est une épée à double tranchant. Si elle offre une grande flexibilité, elle peut aussi introduire des bugs subtils. En comprenant ces pièges et en appliquant les bonnes pratiques présentées, vous pourrez tirer pleinement parti des types mutables tout en évitant leurs écueils.
Objectif pédagogique : Comprendre la mutabilité des objets en Python.
Énoncé :
Un restaurant utilise une liste partagée pour enregistrer les commandes. Observez ce qui se passe.
def nouvelle_commande(commande):
plats = ["eau", "pain"]
commande.append(plats)
return commande
cmd1 = []
cmd2 = nouvelle_commande(cmd1)
cmd2[0].append("vin")
print(cmd1)
plats
) à la liste commande
. Mais comme plats
est un type mutable, toute modification (comme append("vin")
) affecte aussi l'objet référencé dans cmd1
.[['eau', 'pain', 'vin']]
✅ Bonne pratique : utiliser commande.append(plats.copy())
pour éviter cette modification partagée.
Objectif pédagogique : Identifier les problèmes de mutabilité avec les valeurs par défaut.
Énoncé :
Une école veut ajouter des notes aux élèves. La fonction suivante semble mal se comporter. Pourquoi ?
def ajouter_notes(nom, notes=[]):
notes.append(10)
print(f"{nom} : {notes}")
ajouter_notes("Alice")
ajouter_notes("Bob")
notes
est définie par défaut comme une liste vide. Mais en Python, cette valeur par défaut est partagée entre tous les appels !Alice : [10]
Bob : [10, 10]
✅ Bonne pratique :
def ajouter_notes(nom, notes=None):
if notes is None:
notes = []
notes.append(10)
print(f"{nom} : {notes}")
Objectif pédagogique : Comprendre la gestion des objets mutables dans des structures de données.
Énoncé :
Un jeu ajoute des objets à l’inventaire du joueur. Mais l’inventaire semble se remplir tout seul !
def creer_joueur(nom, inventaire=[]):
inventaire.append("épée")
return { "nom": nom, "inventaire": inventaire }
j1 = creer_joueur("Arthur")
j2 = creer_joueur("Lancelot")
print(j1)
print(j2)
inventaire
est partagée entre les deux joueurs ! Chaque appel modifie la même liste.{'nom': 'Arthur', 'inventaire': ['épée', 'épée']}
{'nom': 'Lancelot', 'inventaire': ['épée', 'épée']}
✅ Bonne pratique : utiliser None
comme valeur par défaut :
def creer_joueur(nom, inventaire=None):
if inventaire is None:
inventaire = []
...
Objectif pédagogique : Identifier les erreurs de modification d'une liste en cours d'itération.
Énoncé :
Une fonction supprime les nombres pairs d'une liste. Le résultat est-il correct ?
nombres = [2, 3, 4, 5, 6]
for n in nombres:
if n % 2 == 0:
nombres.remove(n)
print(nombres)
[3, 5]
✅ Bonne pratique : itérer sur une copie :
for n in nombres[:]: # ou nombres.copy()
if n % 2 == 0:
nombres.remove(n)
Objectif pédagogique : Comprendre la différence entre référence et copie d'une liste.
Énoncé :
Une boutique duplique ses listes de produits mais constate des modifications inattendues.
produits = ["chaussures", "t-shirt"]
copie = produits
copie.append("chapeau")
print(produits)
copie
ne crée pas une copie indépendante mais une nouvelle référence vers la même liste.['chaussures', 't-shirt', 'chapeau']
✅ Bonne pratique : utiliser copie = produits.copy()
ou list(produits)
pour une vraie duplication.
Objectif pédagogique : Comprendre l'utilisation des dictionnaires et la gestion des références en Python.
Énoncé :
Un service de gestion des employés utilise un dictionnaire pour stocker les informations des employés. Observez le comportement de votre code. Répondez aux questions suivantes :
1. Que se passe-t-il si vous modifiez les informations d'un employé après l'avoir ajouté à la liste ?
2. Comment pourriez-vous éviter de modifier les données d'un employé par inadvertance ?
def ajouter_employe(employes, nom, age):
employes[nom] = {"age": age, "projet": None}
employes = {}
ajouter_employe(employes, "Alice", 30)
ajouter_employe(employes, "Bob", 25)
# Modification
employes["Alice"]["age"] = 31
print(employes)
employes[nom] = {"age": age, "projet": None}.copy()
.{'Alice': {'age': 31, 'projet': None}, 'Bob': {'age': 25, 'projet': None}}
Cela montre que les données d'Alice ont été modifiées correctement.
Objectif pédagogique : Comprendre l'impact des listes mutables sur les valeurs par défaut des fonctions.
Énoncé :
Un professeur utilise une fonction pour ajouter des notes à ses étudiants. Cependant, il constate des résultats étranges. Répondez aux questions ci-dessous :
1. Pourquoi les notes de chaque étudiant semblent-elles s'accumuler ?
2. Comment corriger cette fonction pour qu'elle fonctionne comme prévu ?
def ajouter_note(nom, notes=[]):
notes.append(10)
print(f"{nom} : {notes}")
ajouter_note("Claire")
ajouter_note("David")
None
comme valeur par défaut et initialiser une nouvelle liste dans la fonction. Voici comment faire :
def ajouter_note(nom, notes=None):
if notes is None:
notes = []
notes.append(10)
print(f"{nom} : {notes}")
Cela garantit que chaque étudiant a sa propre liste de notes.
Objectif pédagogique : Identifier les problèmes de référence lors de la création d'objets.
Énoncé :
Un jeu vidéo crée des personnages avec un inventaire partagé. Répondez aux questions suivantes :
1. Que se passe-t-il lorsque vous créez plusieurs personnages ?
2. Comment éviter que l'inventaire soit partagé entre les personnages ?
def creer_personnage(nom, inventaire=[]):
inventaire.append("épée")
return {"nom": nom, "inventaire": inventaire}
p1 = creer_personnage("Héros")
p2 = creer_personnage("Mage")
print(p1)
print(p2)
None
comme valeur par défaut pour l'inventaire et créer une nouvelle liste pour chaque personnage :
def creer_personnage(nom, inventaire=None):
if inventaire is None:
inventaire = []
inventaire.append("épée")
return {"nom": nom, "inventaire": inventaire}
Chaque personnage aura ainsi son propre inventaire.
Objectif pédagogique : Identifier les erreurs lors de la modification d'une liste pendant l'itération.
Énoncé :
Une fonction tente de supprimer les nombres pairs d'une liste, mais le résultat n'est pas celui attendu. Répondez aux questions suivantes :
1. Que se passe-t-il lorsque vous modifiez la liste pendant l'itération ?
2. Quelle serait la meilleure méthode pour supprimer des éléments d'une liste sans rencontrer d'erreurs ?
nombres = [1, 2, 3, 4, 5, 6]
for n in nombres:
if n % 2 == 0:
nombres.remove(n)
print(nombres)
nombres = [1, 2, 3, 4, 5, 6]
nombres = [n for n in nombres if n % 2 != 0]
print(nombres)
Cela garantit que vous ne modifiez pas la liste pendant l'itération.
Objectif pédagogique : Comprendre les différences entre les références et les copies profondes d'une liste.
Énoncé :
Une boutique essaie de dupliquer ses listes de produits, mais elle rencontre des modifications inattendues. Répondez aux questions suivantes :
1. Pourquoi les modifications dans la liste copiée apparaissent-elles également dans la liste d'origine ?
2. Quelle méthode pouvez-vous utiliser pour créer une copie indépendante d'une liste ?
produits = ["chaussures", "t-shirt"]
copie = produits
copie.append("chapeau")
print(produits)
copie
ne crée pas une nouvelle liste, mais une référence vers la même liste. Cela signifie que toute modification de copie
affecte également produits
.produits.copy()
ou list(produits)
:
copie = produits.copy()
copie.append("chapeau")
print(produits)
Cela garantit que les modifications apportées à copie
n'affectent pas produits
.
Objectif pédagogique : Comprendre comment gérer des listes et éviter les erreurs de référence.
Énoncé :
Un magasin en ligne utilise une liste pour enregistrer les commandes de ses clients. Lisez le code suivant et répondez aux questions :
1. Que se passe-t-il si plusieurs clients passent des commandes en même temps ?
2. Comment cela pourrait-il affecter l'intégrité des données ?
3. Proposez une solution pour gérer chaque commande indépendamment.
def passer_commande(commande, articles):
commande.append(articles)
return commande
commande_client1 = []
commande_client2 = passer_commande(commande_client1, ["chaussures", "t-shirt"])
commande_client2[0].append("chapeau")
print(commande_client1)
commande_client2
, cela modifie également commande_client1
, car les deux variables pointent vers la même liste. C'est un problème de mutabilité des listes.def passer_commande(commande, articles):
commande.append(articles.copy())
return commande
Ainsi, chaque commande reste indépendante.
Objectif pédagogique : Comprendre la gestion des listes et le calcul des moyennes.
Énoncé :
Un enseignant souhaite calculer la moyenne des notes des étudiants. Examinez le code ci-dessous et répondez aux questions :
1. Pourquoi la moyenne calculée est-elle incorrecte si plusieurs appels sont effectués ?
2. Comment corriger le code pour qu'il fonctionne correctement à chaque appel ?
def ajouter_note(nom, notes=[]):
notes.append(10)
moyenne = sum(notes) / len(notes)
print(f"{nom} : moyenne = {moyenne}")
ajouter_note("Alice")
ajouter_note("Bob")
notes
est partagée entre tous les appels. Ainsi, à chaque nouvel appel, la note de 10 est ajoutée à la même liste, ce qui fausse les calculs de moyenne.None
comme valeur par défaut et initialisez une nouvelle liste pour chaque étudiant :
def ajouter_note(nom, notes=None):
if notes is None:
notes = []
notes.append(10)
moyenne = sum(notes) / len(notes)
print(f"{nom} : moyenne = {moyenne}")
Cela permet de calculer des moyennes indépendantes pour chaque étudiant.
Objectif pédagogique : Identifier les problèmes de référence dans la gestion des objets.
Énoncé :
Un jeu vidéo permet de créer des personnages avec un inventaire. Examinez le code ci-dessous et répondez aux questions :
1. Que se passe-t-il lorsque plusieurs personnages sont créés ?
2. Comment cela affecte-t-il l'inventaire des personnages ?
3. Proposez une solution pour que chaque personnage ait un inventaire indépendant.
def creer_personnage(nom, inventaire=[]):
inventaire.append("épée")
return {"nom": nom, "inventaire": inventaire}
p1 = creer_personnage("Guerrier")
p2 = creer_personnage("Mage")
print(p1)
print(p2)
None
comme valeur par défaut et créer une nouvelle liste pour chaque personnage :
def creer_personnage(nom, inventaire=None):
if inventaire is None:
inventaire = []
inventaire.append("épée")
return {"nom": nom, "inventaire": inventaire}
Chaque personnage aura ainsi son propre inventaire.
Objectif pédagogique : Apprendre à gérer les erreurs lors de la suppression d'éléments d'une liste pendant l'itération.
Énoncé :
Une fonction tente de supprimer les nombres impairs d'une liste, mais le comportement n'est pas celui attendu. Répondez aux questions suivantes :
1. Pourquoi certaines valeurs sont-elles omises lors de l'itération ?
2. Quelle méthode pourriez-vous utiliser pour éviter ce problème ?
nombres = [1, 2, 3, 4, 5, 6]
for n in nombres:
if n % 2 != 0:
nombres.remove(n)
print(nombres)
nombres = [1, 2, 3, 4, 5, 6]
nombres = [n for n in nombres if n % 2 == 0]
print(nombres)
Cela garantit que vous ne modifiez pas la liste pendant l'itération.
Objectif pédagogique : Comprendre les différences entre les références et les copies profondes d'une liste.
Énoncé :
Un magasin souhaite dupliquer sa liste de produits, mais il rencontre des modifications inattendues. Répondez aux questions suivantes :
1. Pourquoi les modifications dans la liste copiée apparaissent-elles également dans la liste d'origine ?
2. Quelle méthode pouvez-vous utiliser pour créer une copie indépendante d'une liste ?
produits = ["chaussures", "t-shirt"]
copie = produits
copie.append("chapeau")
print(produits)
copie
ne crée pas une nouvelle liste, mais une référence vers la même liste. Par conséquent, toute modification de copie
affecte aussi produits
.produits.copy()
ou list(produits)
:
copie = produits.copy()
copie.append("chapeau")
print(produits)
Cela garantit que les modifications apportées à copie
n'affectent pas produits
.
Explorez comment l'art et la philosophie s'entrelacent pour questionner notre perception de la réalité et de l'esthétique.
Read more.Plongez dans les débats philosophiques sur la liberté, ses implications éthiques et les défis contemporains qui l'entourent.
Read more.Découvrez les différentes approches philosophiques de la vérité et comment elles influencent notre compréhension du monde.
Read more.Abonnez-vous maintenant et recevez notre newsletter hebdomadaire avec des matériaux éducatifs, de nouveaux cours, des articles intéressants, des livres populaires et bien plus encore !