Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Licence CC BY-NC-ND Thierry Parmentelat & Arnaud Legout Inria - UCA
%load_ext ipythontutor

Complément - niveau basique

Deux types de copie

Pour résumer les deux grands types de copie que l’on a vus dans la vidéo :

Le module copy

Pour réaliser une copie, la méthode la plus simple, en ceci qu’elle fonctionne avec tous les types de manière identique, consiste à utiliser le module standard copy, et notamment :

import copy
#help(copy.copy)
#help(copy.deepcopy)

Un exemple

Nous allons voir le résultat des deux formes de copie sur un même sujet de départ.

La copie superficielle / shallow copie / copy.copy

N’oubliez pas de cliquer le bouton Next dans la fenêtre pythontutor :

%%ipythontutor height=410 curInstr=6
import copy
# On se donne un objet de départ
source = [
    [1, 2, 3],  # une liste
    {1, 2, 3},  # un ensemble
    (1, 2, 3),  # un tuple
    '123',       # un string
    123,         # un entier
]
# une copie simple renvoie ceci
shallow_copy = copy.copy(source)
Loading...

Vous remarquez que :

On rappelle aussi que, la source étant une liste, on aurait pu aussi bien faire la copie superficielle avec

shallow2 = source[:]

La copie profonde / deep copie / copy.deepcopy

Sur le même objet de départ, voici ce que fait la copie profonde :

%%ipythontutor height=410 curInstr=6
import copy
# On se donne un objet de départ
source = [
    [1, 2, 3],  # une liste
    {1, 2, 3},  # un ensemble
    (1, 2, 3),  # un tuple
    '123',       # un string
    123,         # un entier
]
# une copie profonde renvoie ceci
deep_copy = copy.deepcopy(source)
Loading...

Ici, il faut remarquer que :

On retrouve donc à nouveau l’optimisation qui est mise en place dans python pour implémenter les types immuables comme des singletons lorsque c’est possible. Cela a été vu en détail dans le complément consacré à l’opérateur is.

Complément - niveau intermédiaire

# on répète car le code précédent a seulement été exposé à pythontutor
import copy
source = [
    [1, 2, 3],  # une liste
    {1, 2, 3},  # un ensemble
    (1, 2, 3),  # un tuple
    '123',       # un string
    123,         # un entier
]
shallow_copy = copy.copy(source)
deep_copy = copy.deepcopy(source)

Objets égaux au sens logique

Bien sûr ces trois objets se ressemblent si on fait une comparaison logique avec == :

print('source == shallow_copy:', source == shallow_copy)
print('source == deep_copy:', source == deep_copy)
source == shallow_copy: True
source == deep_copy: True

Inspectons les objets de premier niveau

Mais par contre si on compare l’identité des objets de premier niveau, on voit que source et shallow_copy partagent leurs objets :

# voir la cellule ci-dessous si ceci vous parait peu clair
for i, (source_item, copy_item) in enumerate(zip(source, shallow_copy)):
    compare = source_item is copy_item
    print(f"source[{i}] is shallow_copy[{i}] -> {compare}")
source[0] is shallow_copy[0] -> True
source[1] is shallow_copy[1] -> True
source[2] is shallow_copy[2] -> True
source[3] is shallow_copy[3] -> True
source[4] is shallow_copy[4] -> True
# rappel au sujet de zip et enumerate
# la cellule ci-dessous est essentiellement équivalente à
for i in range(len(source)):
    compare = source[i] is shallow_copy[i]
    print(f"source[{i}] is shallow_copy[{i}] -> {compare}")
The history saving thread hit an unexpected error (OperationalError('no such table: history')).History will not be written to the database.
source[0] is shallow_copy[0] -> True
source[1] is shallow_copy[1] -> True
source[2] is shallow_copy[2] -> True
source[3] is shallow_copy[3] -> True
source[4] is shallow_copy[4] -> True

Alors que naturellement ce n’est pas le cas avec la copie en profondeur :

for i, (source_item, deep_item) in enumerate(zip(source, deep_copy)):
    compare = source_item is deep_item
    print(f"source[{i}] is deep_copy[{i}] -> {compare}")
source[0] is deep_copy[0] -> False
source[1] is deep_copy[1] -> False
source[2] is deep_copy[2] -> True
source[3] is deep_copy[3] -> True
source[4] is deep_copy[4] -> True

On retrouve ici ce qu’on avait déjà remarqué sous pythontutor, à savoir que les trois derniers objets - immuables - n’ont pas été dupliqués comme on aurait pu s’y attendre.

On modifie la source

Il doit être clair à présent que, précisément parce que deep_copy est une copie en profondeur, on peut modifier source sans impacter du tout deep_copy.

S’agissant de shallow_copy, par contre, seuls les éléments de premier niveau ont été copiés. Aussi si on fait une modification par exemple à l’intérieur de la liste qui est le premier fils de source, cela sera répercuté dans shallow_copy :

print("avant, source      ", source)
print("avant, shallow_copy", shallow_copy)
source[0].append(4)
print("après, source      ", source)
print("après, shallow_copy", shallow_copy)
avant, source       [[1, 2, 3], {1, 2, 3}, (1, 2, 3), '123', 123]
avant, shallow_copy [[1, 2, 3], {1, 2, 3}, (1, 2, 3), '123', 123]
après, source       [[1, 2, 3, 4], {1, 2, 3}, (1, 2, 3), '123', 123]
après, shallow_copy [[1, 2, 3, 4], {1, 2, 3}, (1, 2, 3), '123', 123]

Si par contre on remplace complètement un élément de premier niveau dans la source, cela ne sera pas répercuté dans la copie superficielle :

print("avant, source      ", source)
print("avant, shallow_copy", shallow_copy)
source[0] = 'remplacement'
print("après, source      ", source)
print("après, shallow_copy", shallow_copy)
avant, source       [[1, 2, 3, 4], {1, 2, 3}, (1, 2, 3), '123', 123]
avant, shallow_copy [[1, 2, 3, 4], {1, 2, 3}, (1, 2, 3), '123', 123]
après, source       ['remplacement', {1, 2, 3}, (1, 2, 3), '123', 123]
après, shallow_copy [[1, 2, 3, 4], {1, 2, 3}, (1, 2, 3), '123', 123]

Copie et circularité

Le module copy est capable de copier - même en profondeur - des objets contenant des références circulaires.

l = [None]
l[0] = l
l
[[...]]
copy.copy(l)
[[[...]]]
copy.deepcopy(l)
[[...]]

Pour en savoir plus

On peut se reporter à la section sur le module copy dans la documentation Python.