Python Dictionnaires : La Méthode copy Expliquée
Apprenez toutes les façons de copier un dictionnaire Python — copy(), dict(), {**d} et deepcopy() — avec des exemples clairs sur les copies superficielles et profondes.
Ce chapitre explique toutes les façons standard de copier un dictionnaire Python, pourquoi une simple affectation (=) n'est pas une copie, et quand vous avez besoin d'une copie profonde plutôt que d'une copie superficielle.
Pourquoi vous ne pouvez pas copier un dictionnaire avec =
Affecter un dictionnaire à une nouvelle variable ne crée pas de copie — cela crée une deuxième référence vers le même objet. Toute modification effectuée via l'une ou l'autre variable affecte le même dictionnaire sous-jacent.
original = {'a': 1, 'b': 2}
alias = original # same object, not a copy
alias['c'] = 3
print(original) # {'a': 1, 'b': 2, 'c': 3} — original changed!Pour obtenir un dictionnaire véritablement indépendant, vous devez utiliser l'une des méthodes de copie présentées ci-dessous.
Copie superficielle vs. copie profonde
Avant d'examiner les méthodes, il est utile de comprendre les deux types de copie :
| Type | Ce qui est copié | Mutables imbriqués |
|---|---|---|
| Copie superficielle | Paires clé-valeur de premier niveau | Les références sont partagées — une mutation dans un dict affecte l'autre |
| Copie profonde | Chaque objet à chaque profondeur | Totalement indépendant — aucune référence partagée |
Pour les dictionnaires plats (dont les valeurs sont des string, des nombres, None, des boolean), une copie superficielle est toujours suffisante. Pour les dictionnaires contenant des listes, des ensembles ou d'autres dictionnaires comme valeurs, envisagez une copie profonde.
Méthode 1 : dict.copy()
La méthode intégrée copy() renvoie un nouveau dictionnaire qui est une copie superficielle de l'original. C'est le choix le plus idiomatique pour les dictionnaires plats.
Syntaxe
new_dict = original_dict.copy()Créer une copie superficielle d'un dictionnaire en Python
Ajouter ou supprimer une clé dans new_dict laisse original_dict intact, car la copie de premier niveau est indépendante.
Mise à jour des clés de premier niveau dans un dictionnaire copié
Méthode 2 : le constructeur dict()
Passer un dictionnaire existant au constructeur dict() crée une copie superficielle de la même façon que copy().
original_dict = {'name': 'Alice', 'age': 30}
new_dict = dict(original_dict)
new_dict['age'] = 31
print(original_dict) # {'name': 'Alice', 'age': 30}
print(new_dict) # {'name': 'Alice', 'age': 31}dict() est utile lorsque vous souhaitez être explicite sur la création d'un dictionnaire à partir d'un autre mapping ou lorsque vous combinez des arguments nommés avec un dictionnaire existant :
defaults = {'color': 'blue', 'size': 'M'}
custom = dict(defaults, size='L', weight='light')
print(custom)
# {'color': 'blue', 'size': 'L', 'weight': 'light'}Méthode 3 : décomposition de dictionnaire {**d}
L'opérateur de décomposition ** fusionne un dictionnaire dans un nouveau littéral de dictionnaire. Pour une simple copie, il se comporte de façon identique à copy(), mais il permet également de fusionner plusieurs dictionnaires ou de remplacer des clés individuelles en une seule expression.
original_dict = {'a': 1, 'b': 2}
new_dict = {**original_dict}
new_dict['c'] = 3
print(original_dict) # {'a': 1, 'b': 2}
print(new_dict) # {'a': 1, 'b': 2, 'c': 3}Copier en remplaçant une clé
config = {'host': 'localhost', 'port': 5432, 'debug': False}
prod_config = {**config, 'host': 'db.example.com', 'debug': False}
print(prod_config)
# {'host': 'db.example.com', 'port': 5432, 'debug': False}Comme copy(), il s'agit d'une copie superficielle — les mutables imbriqués restent partagés.
Piège de la copie superficielle : les mutables imbriqués sont partagés
Les trois méthodes ci-dessus produisent des copies superficielles. Lorsqu'une valeur est elle-même un objet mutable (comme une liste), l'original et la copie contiennent tous deux une référence vers le même objet interne.
Mise à jour de valeurs mutables dans un dictionnaire copié
Les deux dictionnaires reflètent la modification parce que new_dict['key1'] et original_dict['key1'] pointent vers le même objet liste. Il s'agit d'un comportement attendu pour une copie superficielle — ce n'est pas un bug.
Méthode 4 : copy.deepcopy() pour une indépendance totale
Lorsque vous avez besoin d'un isolement complet — y compris des listes, des ensembles et des dictionnaires imbriqués — utilisez copy.deepcopy() du module standard copy. Il copie récursivement chaque objet de la structure.
Copie profonde en Python
deepcopy() gère les structures imbriquées de manière arbitraire et même les objets auto-référentiels. Son compromis est la vitesse et la mémoire : elle est plus lente qu'une copie superficielle car elle doit parcourir et dupliquer l'intégralité du graphe d'objets.
Choisir la bonne méthode
| Situation | Méthode recommandée |
|---|---|
| Dictionnaire plat (sans mutables imbriqués) | dict.copy() ou {**d} |
| Fusionner / remplacer des clés lors de la copie | {**d, key: value} ou dict(d, key=value) |
| Mutables imbriqués, besoin d'une indépendance totale | copy.deepcopy() |
| Convertir un autre mapping en copie de dictionnaire | dict(mapping) |
Résumé
- L'opérateur
=crée un alias, pas une copie. dict.copy(),dict()et{**d}produisent tous des copies superficielles — les clés de premier niveau sont indépendantes, mais les mutables imbriqués sont partagés.copy.deepcopy()produit une copie profonde — chaque objet à chaque profondeur est dupliqué.- Pour les dictionnaires plats, préférez
dict.copy()pour sa clarté. - Utilisez
{**d, overrides}lorsque vous devez copier et modifier en une seule expression.
Voir aussi : Méthodes de dictionnaire · Dictionnaires imbriqués · Parcourir des dictionnaires