ProgrammationDéveloppeur Python Backend

Quelle est la différence entre une copie superficielle et une simple attribution de variable ? Comment fonctionne copy.copy() en ce qui concerne les structures de données imbriquées (listes dans des listes) ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

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 piège

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

Exemples d'erreurs réelles dues à l'incompréhension des subtilités de ce sujet


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.