Comprendre les copies et références en Python : =, copy() et deepcopy()
Apprenez à éviter les modifications inattendues en Python en distinguant affectation, copie superficielle et copie profonde.

- Difficulté
- Facile
Pour éviter les modifications inattendues en Python, il faut retenir une règle simple : = ne copie pas, tandis que copy() et deepcopy() ne copient pas au même niveau. Le bon choix dépend surtout de la présence ou non de listes et dictionnaires imbriqués.
Ce guide montre quand deux variables pointent vers le même objet, quand une copie reste partagée en profondeur, et comment vérifier ce qui se passe avec de petits tests.
Prérequis
Savoir créer une liste, un dictionnaire et exécuter un script Python. Les exemples suivent le comportement décrit dans la documentation officielle de Python pour l'affectation et le module copy.[1][2]
Matériel nécessaire
- Python 3 installé
- Un éditeur de texte ou un terminal Python
Étapes
Tester l'affectation avec =

Commencez par créer une liste, puis affectez-la à une seconde variable. Modifiez ensuite la seconde variable et observez que la première change aussi.
a = [1, 2, 3] b = a b.append(4) print(a) # [1, 2, 3, 4] print(b) # [1, 2, 3, 4]Ici, a et b référencent le même objet en mémoire. La documentation Python précise qu'une affectation lie un nom à un objet ; elle ne crée pas une copie.[1]
Créer une copie simple d'une liste ou d'un dictionnaire
Utilisez copy() quand vous voulez dupliquer seulement le conteneur principal. Cela suffit si votre liste ou dictionnaire ne contient pas d'autres objets modifiables imbriqués.
liste1 = [1, 2, 3] liste2 = liste1.copy() liste2.append(4) print(liste1) # [1, 2, 3] print(liste2) # [1, 2, 3, 4] dict1 = {"a": 1, "b": 2} dict2 = dict1.copy() dict2["c"] = 3 print(dict1) # {"a": 1, "b": 2} print(dict2) # {"a": 1, "b": 2, "c": 3}La documentation des types intégrés indique que list.copy() et dict.copy() retournent une copie superficielle.[3][4]
Modifier un objet imbriqué pour repérer une copie superficielle

Faites maintenant le test avec une liste imbriquée. Vous verrez que copy() sépare le niveau extérieur, mais conserve les mêmes objets internes.
a = [[1, 2], [3, 4]] b = a.copy() b[0].append(99) print(a) # [[1, 2, 99], [3, 4]] print(b) # [[1, 2, 99], [3, 4]]Le conteneur b est nouveau, mais b[0] référence encore la même liste que a[0]. C'est exactement la définition d'une copie superficielle donnée par le module copy.[2]
Importer deepcopy pour dupliquer toute la structure

Utilisez deepcopy() quand votre structure contient des listes ou dictionnaires imbriqués que vous voulez rendre totalement indépendants. Cette fonction se trouve dans le module standard copy.[2]
from copy import deepcopy a = [[1, 2], [3, 4]] b = deepcopy(a) b[0].append(99) print(a) # [[1, 2], [3, 4]] print(b) # [[1, 2, 99], [3, 4]]Cette fois, la modification de b[0] ne touche pas a[0]. deepcopy() copie récursivement les objets contenus, selon les règles du module copy.[2]
Choisir la bonne méthode selon le cas
Appliquez une règle simple au moment d'écrire votre code. Le choix dépend du niveau d'indépendance attendu entre les deux variables.
- Utilisez = si vous voulez partager exactement le même objet.
- Utilisez copy() si vous voulez un nouveau conteneur sans objets imbriqués à isoler.
- Utilisez deepcopy() si vous voulez une structure entièrement indépendante.
Exemple courant : pour dupliquer une liste plate d'entiers, copy() suffit. Pour dupliquer un dictionnaire de listes, préférez deepcopy() si chaque copie doit évoluer séparément.
Erreurs fréquentes & dépannage
Erreur classique : croire que b = a crée une copie. En réalité, vous obtenez deux noms pour le même objet.
Erreur fréquente : penser que copy() protège aussi les listes et dictionnaires internes. Ce n'est pas le cas pour les objets imbriqués modifiables.
Erreur de diagnostic : tester seulement l'égalité avec ==. Deux objets peuvent être égaux en contenu sans être indépendants ; utilisez aussi is pour raisonner sur l'identité.
Astuces & pour aller plus loin
Pour une liste, vous pouvez aussi rencontrer la notation de tranche complète a[:], qui produit également une copie superficielle de la liste selon la documentation Python.[3] Le point important reste le même : ce n'est pas une copie profonde.
Si vous manipulez des données imbriquées dans des fonctions, annoncez clairement si la fonction modifie l'objet reçu ou travaille sur une copie. Cela évite beaucoup d'effets de bord difficiles à repérer.
Questions fréquentes
copy() et deepcopy() fonctionnent-ils seulement avec les listes ?
Non. Le module copy s'applique à de nombreux objets Python, et les dictionnaires ont aussi leur méthode copy(). Le comportement exact dépend du type d'objet concerné.[2][4]
Pourquoi une liste d'entiers semble bien copiée avec copy() ?
Parce que le problème visible apparaît surtout avec des objets modifiables imbriqués. Avec une liste plate d'entiers, une copie superficielle suffit généralement en pratique.
Faut-il toujours utiliser deepcopy() par sécurité ?
Non. deepcopy() est utile quand vous devez isoler toute la structure, mais il ajoute un travail supplémentaire inutile si une simple copie superficielle suffit.[2]
Conclusion
Retenez ce réflexe : si vos données contiennent des structures imbriquées modifiables, vérifiez tout de suite si une copie superficielle suffit vraiment. En cas de doute, testez avec une modification interne ; vous saurez immédiatement s'il faut rester avec copy() ou passer à deepcopy().
Sources : [1] Documentation Python, section sur les affectations : https://docs.python.org/3/reference/simple_stmts.html#assignment-statements ; [2] Documentation Python, module copy : https://docs.python.org/3/library/copy.html ; [3] Documentation Python, type list : https://docs.python.org/3/tutorial/datastructures.html et https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types ; [4] Documentation Python, type dict : https://docs.python.org/3/library/stdtypes.html#dict.copy