L'attribution de variable (par exemple, a = b) en Python ne copie pas l'objet lui-même, mais crée simplement un nouveau nom (référence) vers l'objet existant. Les modifications via un nom seront visibles à travers l'autre si c'est un objet mutable.
La copie superficielle (shallow copy), par exemple via copy.copy(obj) ou une tranche [:] pour une liste, crée un nouvel objet de premier niveau, mais les objets imbriqués à l'intérieur sont copiés par référence (c'est-à-dire que les deux structures "regardent" les mêmes sous-conteneurs). Si on modifie les objets imbriqués, les modifications seront visibles à travers les deux objets.
Exemple :
import copy lst1 = [[1,2], [3,4]] lst2 = copy.copy(lst1) # ou lst1[:] lst1[0][0] = 100 print(lst2) # [[100, 2], [3, 4]]
lst2 est une nouvelle liste, mais son premier élément est la même liste imbriquée.
Question : Quelle est la différence entre lst2 = lst1[:] et lst2 = copy.copy(lst1) ?
Réponse : En pratique, pour des listes ordinaires (à un niveau), il n'y a pas de différence — les deux méthodes font une copie superficielle de la liste. Cependant, pour des classes de conteneurs personnalisées, le comportement peut différer (par exemple, si une méthode __copy__ est implémentée). De même, pour d'autres types (dict, set, etc.), utiliser le module spécialisé copy est plus sûr.
import copy lst1 = [1, 2, 3] lst2 = lst1[:] lst3 = copy.copy(lst1) print(lst2 == lst3) # True
Histoire
Dans un projet de traitement de configuration, le développeur a dupliqué les paramètres par défaut par attribution params = default_params et pensait pouvoir les modifier "de manière isolée". En fin de compte, toute modification dans une copie entraînait une cascade de changements dans toutes les parties de l'application, car il travaillait en réalité avec un seul objet.
Histoire
Un programmeur inexpérimenté a utilisé une copie superficielle de la liste pour stocker l'état du jeu (game_states = states[:]). Avec une structure imbriquée (listes des pièces sur le plateau), des modifications à l'intérieur d'un état "fuitaient" vers d'autres, perturbant l'historique des annulations et des répétitions de mouvements.
Histoire
Lors d'une tentative de clonage des données dans une application orientée objet, le choix était entre une tranche et copy.copy(). Mais dans la structure, une de ses classes avait sa propre méthode de copie, qui n'était prise en compte que lors de l'utilisation de copy.copy(). La tranche a ignoré la logique de copie de l'objet, entraînant des bugs non évidents.