Bases

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.

Comprendre les copies et références en Python : =, copy() et deepcopy()
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

  1. Tester l'affectation avec =

    Vue de dessus d’un ordinateur portable sur un bureau propre, avec deux ensembles identiques affichés à l’écran et deux petits plateaux contenant les mêmes objets disposés de la même façon.

    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]

  2. 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]

  3. Modifier un objet imbriqué pour repérer une copie superficielle

    Gros plan sur deux plateaux similaires avec un contenant principal et un petit contenant imbriqué ; une main déplace des objets dans le petit contenant de l’un et l’autre présente la même modification.

    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]

  4. Importer deepcopy pour dupliquer toute la structure

    Gros plan sur deux plateaux similaires avec de petits contenants imbriqués ; une main modifie le contenu de l’un tandis que l’autre reste inchangé, montrant une duplication totalement indépendante.

    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]

  5. 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

Commentaires · Aucun commentaire pour l'instant

Soyez le premier à réagir.

Laisser un commentaire