une surprise en modifiant des listes

Le
michelc
Bonjour,

commençant de bidouiller depuis quelques semaines avec Python 2.5, je
suis tombé sur la surprise suivante.

>>> =
== RESTART

>>> old = ['abcdef','ghijkl']
>>> new = old
>>> old
['abcdef', 'ghijkl']
>>> new
['abcdef', 'ghijkl']
>>> new[1]= new[1][0:2] +'zz'+new[1][4:]
>>> new
['abcdef', 'ghzzkl']
>>> old
['abcdef', 'ghzzkl']
>>>

Autrement dit la modification d'un élément de la liste new entraine la
modification à l'identique de la liste old
bien que old n'apparaisse jamais à gauche d'une égalité après sa
création.
Cette surprenante propriété ne me semble pas clairement documentée
dans le tutoriel que j'utilise.
Elle concerne les listes, mais pas les objets plus simples : si old
est une simple chaine, on la retrouve inchangée à la fin des
opérations :

>>> =
== RESTART

>>> old = 'abcdef'
>>> new = old
>>> old
'abcdef'
>>> new
'abcdef'
>>> new= new[0:2] +'zz'+new[4:]
>>> new
'abzzef'
>>> old
'abcdef'
>>>

Si, comme je le suppose, il s'agit là d'une propriété naturelle connue=

des listes, quelqu'un peut -il me dire comment faire pour conserver la
liste old sans modification pour pouvoir créer plusieurs listes
new1,new2,new3 en applicant des altérations différentes à la même=

base old

Merci d'avance à qui pourra m'éclairer
Cordialement

Michelc
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Bruno Desthuilliers
Le #664417
Bonjour,

commençant de bidouiller depuis quelques semaines avec Python 2.5, je
suis tombé sur la surprise suivante.

old = ['abcdefn','ghijkln']
new = old
old
['abcdefn', 'ghijkln']



new
['abcdefn', 'ghijkln']



new[1]= new[1][0:2] +'zz'+new[1][4:]
new
['abcdefn', 'ghzzkln']



old
['abcdefn', 'ghzzkln']




Autrement dit la modification d'un élément de la liste new entraine la
modification à l'identique de la liste old
bien que old n'apparaisse jamais à gauche d'une égalité après sa
création.
Cette surprenante propriété ne me semble pas clairement documentée
dans le tutoriel que j'utilise.


Cette "surprenante propriété" concerne *tous* les objets *sans
exception* (si si). Elle s'explique par le fait qu'en Python, une
"variable" est en fait une paire nom=>référence objet dans un espace de
nommage (généralement une table de hachage). L'assignation consiste donc
en fait a associer un nom à une référence sur un objet. Un même objet
peut bien sûr être associé à plusieurs noms simultanément (dans un même
espace de nommage ou dans des espaces de nommage différents), ce qui est
le cas ci-dessus : 'old' et 'new' pointent tous les deux sur le même
objet. Tu peux le vérifier avec le test d'identité:
print new is old
=> True

Par ailleurs, les accès indexés (listes, dicts etc) sont traduits en
appels aux méthodes __getitem__ et __setitem__, la seconde modifiant
l'état de l'objet.

Bref, ce que tu observes est parfaitement normal : que tu y accèdes par
le nom 'new' ou par le nom 'old', tu travailles sur un seul et même objet.

En bref : Python ne copie jamais rien si tu ne le demandes pas
explicitement (cf plus bas).

Elle concerne les listes, mais pas les objets plus simples : si old
est une simple chaine, on la retrouve inchangée à la fin des
opérations :


Attention, tes deux snippets sont très différents : dans le premier cas,
tu *modifies* un objet existant, dans le second tu *réaffectes* un
nouvel objet à un nom existant.

Accessoirement, les chaines sont immutables (comme les nombres et les
tuples), tu ne peux donc pas faire un test équivalent au premier !-)

old = 'abcdefn'
new = old
old
'abcdefn'



new
'abcdefn'



new= new[0:2] +'zz'+new[4:]
new
'abzzefn'



old
'abcdefn'






Si, comme je le suppose, il s'agit là d'une propriété naturelle connue
des listes, quelqu'un peut -il me dire comment faire pour conserver la
liste old sans modification pour pouvoir créer plusieurs listes
new1,new2,new3... en applicant des altérations différentes à la même
base old


En faisant une copie de la liste. La solution la plus simple:

old = [range(3), range(3,6)]
old
[[0, 1, 2], [3, 4, 5]]



new = old[:]
new[0] = "allo"
new
['allo', [3, 4, 5]]



old
[[0, 1, 2], [3, 4, 5]]




Attention toutefois, c'est une copie "superficielle" - new[1] et old[1]
référencent le même objet :

new[1][0] = 42
new
['allo', [42, 4, 5]]



old
[[0, 1, 2], [42, 4, 5]]





Si tu veux une copie "profonde" (c'est à dire une copie non seulement de
la liste mais aussi de tous les objets qu'elle contient), regarde du
côté de copy.deepcopy()


HTH




Publicité
Poster une réponse
Anonyme