Rappelons que quand on crée une variable, donc lorsqu’on lie un identificateur à un objet par l’instruction nom=obj, on associe en fait à cet identificateur l’adresse en mémoire où se trouve physiquement l’objet. On exprime cette situation en disant qu’on crée une référence, ou encore un pointeur, vers l’objet.
Il y a une différence importante entre les listes d’une part, et les chaînes et tuples de l’autre : les listes sont dites mutables, mais les chaînes et les tuples ne le sont pas.
Dire qu’un objet est mutable, c’est dire qu’on peut en modifier (voire en supprimer) un (ou plusieurs) élément(s), sans pour autant créer une nouvelle référence vers l’objet ainsi modifié (l’adresse du début de l’objet reste inchangée).
Par exemple, si on pose x = y = [5, 2, 9], on définit deux variables x et y contenant en fait l’adresse d’une unique liste. On peut poser x[1] = 10 : on mute donc la liste dont le contenu est maintenant [5, 10, 9], mais dont l’adresse ne change pas en mémoire. Les variables x et y pointent donc toujours vers la même adresse, à laquelle on trouve la liste [5, 10, 9] (pour l’utilisateur, tout se passe donc comme si la mutation de la liste x s’était répercutée sur la liste y).
Le caractère mutable des listes peut réserver quelques surprises. Considérons l’exemple suivant :
|
1 2 3 4 5 6 7 8 9 10 11 12 |
>>> x = [1,2,3]; id(x) # la variable x contient en fait l'adresse de la liste [1,2,3] 4301154856 >>> y = [x,x,x,x]; id(y) # la variable y contient l'adresse de la liste [x,x,x,x] 4359646312 >>> [id(e) for e in y] # les éléments de la liste y pointent l'adresse de la liste [1,2,3] [4301154856, 4301154856, 4301154856, 4301154856] >>> x = [4,5,6]; id(x) # on redéfinit le contenu de x. L'adresse change! 4359645952 >>> y # mais le contenu de la liste y n'a pas changé [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]] >>> [id(e) for e in y] # normal car les adresses pointent toujours sur la liste [1,2,3] [4301154856, 4301154856, 4301154856, 4301154856] |
Reprenons l’exemple précédent, en le modifiant légèrement :
|
1 2 3 4 5 6 7 8 9 10 11 12 |
>>> x = [1,2,3]; id(x) # la variable x contient l'adresse de la liste [1,2,3] 4359646384 >>> y = [x,x,x,x]; id(y) # la variable y contient l'adresse de la liste [x,x,x,x] 4359646240 >>> [id(e) for e in y] # les éléments de la liste y pointent l'adresse de la liste [1,2,3] [4359646384, 4359646384, 4359646384, 4359646384] >>> x[0] = 9; id(x) # on mute la liste x, on ne la redéfinit pas: adresse inchangée! 4359646384 >>> y # toutes les composantes de y ont changé en même temps [[9, 2, 3], [9, 2, 3], [9, 2, 3], [9, 2, 3]] >>> [id(e) for e in y] # normal car elles pointent la même adresse, où on a maintenant [9,2,3] [4359646384, 4359646384, 4359646384, 4359646384] |
Dans l’exemple suivant, on place dans la variable z une liste contenant quatre exemplaires de la liste [1, 2, 3].
Dans un premier temps, on redéfinit l’élément z[0]. Cela n’a pas d’impact sur z[1], z[2] et z[3].
Mais si on mute l’élément z[1], par exemple, cette mutation se répercute sur les éléments z[2] et z[3] (car z[1], z[2] et z[3] continuent à pointer sur la même adresse).
|
1 2 3 4 5 6 7 8 9 |
>>> z = [[1,2,3]] * 4 # on définit z par concaténation de quatre listes identiques >>> z [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]] >>> z[0] = [5,6] # ici on redéfinit l'élément z[0] >>> z # ça n'a pas d'influence sur z[1], z[2] et z[3] [[5, 6], [1, 2, 3], [1, 2, 3], [1, 2, 3]] >>> z[1][2] = 2013 # ici on mute l'élément z[1] >>> z # ça se répercute sur z[2] et z[3] [[5, 6], [1, 2, 2013], [1, 2, 2013], [1, 2, 2013]] |