Clonage de variables de type composé
Impossible d'accéder à la ressource audio ou vidéo à l'adresse :
La ressource n'est plus disponible ou vous n'êtes pas autorisé à y accéder. Veuillez vérifier votre accès puis recharger la vidéo.
Objectifs
Savoir copier des valeurs en JavaScript et en python ;
Connaître la différence entre copie et copie récursive.
Mise en situation
Nous avons appris à copier une variable, et cela n'a rien de très compliqué. Cependant cela est valable pour les variables simples, et c'est une autre affaire quand il s'agit de variables composées, comme des enregistrements. Par exemple si l'on copie un tableau d'enregistrements, qui contiennent eux même des enregistrements, est-ce que les enregistrements imbriqués sont dupliqués ou est-ce que la copie du tableau ne fera que de simples références ? On sent bien que ici la copie des variables est un peu plus compliquée à appréhender.
Exemple : Une erreur commune
Parfois on souhaite copier des valeurs entre différentes variables. Pour cela on utilise généralement l'affectation. Par exemple en Python :
tokens = [1, 2, 3, 4, 5]
numbers = tokens
print(numbers)
[1, 2, 3, 4, 5]
Néanmoins, si on modifie le « premier tableau », le « second » est aussi modifié :
tokens[2] = 98
print(numbers)
[1, 2, 98, 4, 5]
On obtient le même comportement en JavaScript :
let tokens = [1, 2, 3, 4, 5]
let numbers = tokens
tokens[2] = 98
console.log(numbers)
[1, 2, 98, 4, 5]
Pourquoi est-ce que cela se produit ? Comment y remédier ?
Fondamental : Explication : référencement de valeur de type composé
Lors de l'affectation d'une variable par une autre, on recopie dans le cas du type composé la référence vers la valeur.

Ainsi, si on change la variable à l'aide d'une des deux variables, l'autre variable renverra la valeur mise à jour et non l'ancienne.

La valeur 3 n'est plus référencée : une nouvelle valeur 98 est créée, et référencée dans le tableau.
Exemple : Explication de l'exemple précédent
On peut voir que les deux variables en Python référencent la même valeur :
print(id(tokens) == id(numbers))
True
On peut voir cela aussi cela en JavaScript avec le test d'égalité avec l'opérateur ===
.
print(tokens === numbers)
true
Fondamental : Réaliser une copie
Pour réaliser une copie d'une valeur d'un type composé il faut utiliser des fonctions dédiées qui s'occupent de réaliser la copie des variables de types primitifs.
Exemple : Réaliser une copie en Python
En Python on utilise la fonction copy
du module copy
.
import copy
tokens = [1, 2, 3, 4, 5]
numbers = copy.copy(tokens)
numbers
et tokens
référencent deux valeurs différentes :
print(id(tokens) == id(numbers))
False
Ainsi, si on modifie la valeur d'un variable l'autre n'est pas impactée :
tokens[2] = 98
print(tokens, numbers)
[1, 2, 98, 4, 5], [1, 2, 3, 4, 5]
Exemple : Réaliser une copie en JavaScript
En JavaScript, on utilise la méthode Object.assign()
pour réaliser une copie.
let tokens = [1, 2, 3, 4, 5]
let numbers = Object.assign([], tokens)
Le comportement est identique à celui rencontré avec Python:
tokens[2] = 98
console.log(tokens, numbers)
[1, 2, 98, 4, 5], [1, 2, 3, 4, 5]
Complément : Copie récursive de valeurs
Lors d'une copie, seules les premières valeurs superficielles sont copiées, ainsi dans le cas de structures imbriquées (comme un tableau de tableau), toutes les valeurs ne sont pas copiées.
Pour recopier entièrement toutes les valeurs présentes, il faut utiliser une copie récursive.
Exemple : Copie récursive de valeurs en Python
En Python, on peut utiliser la fonction copy.deepcopy
qui réalise une copie récursive de valeurs.
import copy
cours = {
'id_cours': 1337,
'nom_cours': 'IngDoc',
'theme': 'Ingénierie Documentaire',
'etudiants': [
{
'nom': 'Norris',
'prenom': 'Chuck',
'age': 73,
'pays': 'USA'
},
{
'nom': 'Doe',
'prenom': 'Jane',
'age': 45,
'pays': 'Angleterre'
}
]
}
copie_cours = copy.deepcopy(cours)
Remarque : Copie récursive de valeurs en JavaScript
En JavaScript (ES6), il n'est pas nativement possible de réaliser de copie récursive : il faut définir une fonction dédiée ou utiliser une bibliothèque spécialisée.
Exemple : Copie récursive en JavaScript avec une fonction dédiée
On définit la fonction suivante permettant de réaliser une copie récursive :
const deepCopyFunction = (inObject) => {
let outObject, value, key
if (typeof inObject !== "object" || inObject === null) {
// Retourne la valeur de inObject si ce n'est pas un objet
return inObject
}
// Crée un tableau ou un objet pour contenir les valeurs
outObject = Array.isArray(inObject) ? [] : {}
for (key in inObject) {
value = inObject[key]
// Copie récursivement les objets imbriquées, donc les tableaux
outObject[key] = deepCopyFunction(value)
}
return outObject
}
const cours = {
'id_cours': 1337,
'nom_cours': 'IngDoc',
'theme': 'Ingénierie Documentaire',
'etudiants': [
{
'nom': 'Norris',
'prenom': 'Chuck',
'age': 73,
'pays': 'USA'
},
{
'nom': 'Doe',
'prenom': 'Jane',
'age': 45,
'pays': 'Angleterre'
}
]
}
const copie_cours = deepCopyFunction(cours)
cours.etudiants[0].id_cours = 1987
console.log(cours, copie_cours)
Ici, seul l'identifiant (id
) du premier cours est modifié.
{
id_cours: 1987,
nom_cours: 'IngDoc',
theme: 'Ingénierie Documentaire',
etudiants: [
{ nom: 'Norris', prenom: 'Chuck', age: 73, pays: 'USA' },
{ nom: 'Doe', prenom: 'Jane', age: 45, pays: 'Angleterre' }
]
} {
id_cours: 1337,
nom_cours: 'IngDoc',
theme: 'Ingénierie Documentaire',
etudiants: [
{ nom: 'Norris', prenom: 'Chuck', age: 73, pays: 'USA' },
{ nom: 'Doe', prenom: 'Jane', age: 45, pays: 'Angleterre' }
]
}
Complément : Éléments de documentation de copie
Pour Python : https://docs.python.org/3/library/copy.html
Pour JavaScript : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Code source original de la fonction de copie récursive : https://gist.github.com/djD-REK/e8b1497e7fbf0374e4eada669e5609cf#file-custom-deep-copy-function-using-recursion-js
À retenir
Pour copier la valeur d'une variable dans une autre, il faut utiliser les fonctions de copie : copy.copy
en python et Object.assign
en JavaScript.
Pour réaliser une copie intégrale d'une valeur, il faut utiliser les fonctions ou syntaxes de copie récursive : copy.deepcopy
en Python et mettre au point une fonction dédiée en JavaScript.
Impossible d'accéder à la ressource audio ou vidéo à l'adresse :
La ressource n'est plus disponible ou vous n'êtes pas autorisé à y accéder. Veuillez vérifier votre accès puis recharger la vidéo.