HOWTO · Python
Comment copier un dictionnaire en Python
Découvrez quand une affectation crée un alias vers un dictionnaire, quand une copie superficielle suffit et quand une copie profonde est nécessaire pour rendre ses valeurs imbriquées indépendantes.
Sur cette page
Pour créer un nouveau dictionnaire externe, utilisez dict(source) ou source.copy(). Ces deux copies sont superficielles : les valeurs imbriquées restent partagées. Utilisez copy.deepcopy(source) lorsque les valeurs mutables imbriquées doivent également être indépendantes. Une affectation avec = ne crée aucune copie. Pour en savoir plus, consultez le tutoriel sur les dictionnaires Python et la documentation du module copy de Python.
Une affectation crée un alias vers le dictionnaire
Une affectation associe un autre nom au même objet dictionnaire. Toute modification du dictionnaire effectuée par l’un ou l’autre nom est visible à travers les deux. Réaffecter un nom est différent : ce nom pointe alors ailleurs, sans modifier l’objet ni l’autre nom.
Dans cet exemple complet, le dictionnaire partagé est modifié via meal, puis meal est réaffecté à un nouveau dictionnaire :
food = {"Fruit": "Orange", "Vegetable": "Lettuce"}
meal = food
meal["Fruit"] = "Banana"
print(food)
print(meal)
meal = {"Fruit": "Apple"}
print(food)
print(meal)
{'Fruit': 'Banana', 'Vegetable': 'Lettuce'}
{'Fruit': 'Banana', 'Vegetable': 'Lettuce'}
{'Fruit': 'Banana', 'Vegetable': 'Lettuce'}
{'Fruit': 'Apple'}
Les deux premières valeurs affichées sont identiques, car les deux noms désignent alors le même objet. Après la réaffectation de meal, seul ce nom pointe vers le nouveau dictionnaire ; food désigne toujours l’original. Cette distinction explique pourquoi modifier une valeur par l’intermédiaire d’un nom est visible via l’autre, alors que réaffecter une variable ne remplace pas l’objet référencé par l’autre nom.
Créer une copie superficielle d’un dictionnaire
Le constructeur intégré dict() et la méthode copy() d’un dictionnaire créent chacun un nouveau dictionnaire externe. Modifier ou supprimer une entrée de premier niveau dans cette copie ne modifie pas l’original. Les deux dictionnaires référencent toutefois les mêmes listes, dictionnaires ou autres valeurs mutables imbriqués ; les modifications apportées à ces valeurs peuvent donc être visibles via les deux noms.
Ici, by_constructor et by_method ont chacun leur propre dictionnaire externe. La liste imbriquée reste partagée, tandis qu’attribuer une nouvelle valeur à la clé name ne modifie que la copie choisie :
source = {"name": "Sherlock", "numbers": [1, 2, 3]}
by_constructor = dict(source)
by_method = source.copy()
by_constructor["name"] = "Holmes"
by_constructor["numbers"][1] = 4
print(source)
print(by_constructor)
print(by_method)
{'name': 'Sherlock', 'numbers': [1, 4, 3]}
{'name': 'Holmes', 'numbers': [1, 4, 3]}
{'name': 'Sherlock', 'numbers': [1, 4, 3]}
Choisissez l’une ou l’autre de ces formes de copie superficielle lorsque le dictionnaire externe doit être indépendant et que le partage des valeurs imbriquées est acceptable. Une copie superficielle ne copie pas récursivement les objets imbriqués. Par exemple, attribuer une autre valeur à by_constructor["name"] laisse inchangée la valeur de name dans la source, car cette clé appartient à chaque dictionnaire externe distinct. En revanche, modifier un élément de numbers est visible dans les trois dictionnaires affichés, puisque la liste elle-même est partagée. Le même principe s’applique lorsqu’une valeur est un autre dictionnaire : la copie de premier niveau conserve une référence vers ce dictionnaire interne. Pour isoler une seule valeur imbriquée, vous pouvez copier cette valeur séparément ; utilisez une copie profonde si toute la structure imbriquée doit être indépendante.
Créer une copie profonde d’un dictionnaire imbriqué
Lorsque les valeurs mutables imbriquées doivent également être copiées indépendamment, utilisez copy.deepcopy(). Cette fonction copie récursivement les objets contenus ; l’exemple ci-dessous peut donc modifier un élément de liste et une valeur d’un dictionnaire interne sans changer l’original. Cette approche est utile pour les données imbriquées que vous comptez modifier indépendamment, mais elle est inutile si le partage des valeurs imbriquées est souhaité.
import copy
source = {
"numbers": [1, 2, 3],
"address": {"city": "Miami"},
}
independent = copy.deepcopy(source)
independent["numbers"][1] = 4
independent["address"]["city"] = "Lexington"
print(source)
print(independent)
{'numbers': [1, 2, 3], 'address': {'city': 'Miami'}}
{'numbers': [1, 4, 3], 'address': {'city': 'Lexington'}}
Une copie profonde est utile lorsque les modifications ultérieures ne doivent pas se répercuter entre une structure source et sa copie de travail, par exemple pour préparer une version de paramètres imbriqués à modifier indépendamment. Elle copie aussi les conteneurs imbriqués, et pas seulement le dictionnaire externe ; c’est pourquoi la liste et le dictionnaire address interne restent inchangés ci-dessus. Pour de grandes structures, une copie profonde peut demander plus de temps et de mémoire qu’une copie superficielle : choisissez-la selon que vous avez besoin ou non d’objets imbriqués indépendants. Les objets Python peuvent personnaliser leur comportement de copie, et certaines ressources ne peuvent pas être dupliquées de façon pertinente ; consultez la documentation du module copy si vous travaillez avec de tels objets.
En pratique, le choix dépend des objets qui doivent rester partagés : une affectation partage le dictionnaire entier, une copie superficielle ne sépare que le dictionnaire externe, et une copie profonde sépare aussi les objets imbriqués. La documentation du module copy explique comment les copies superficielles et profondes traitent les objets composites et ceux dont le comportement de copie est personnalisé.