une surprise en modifiant des listes

Le
michelc
Bonjour,

commenant 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 lment de la liste new entraine la
modification l'identique de la liste old
bien que old n'apparaisse jamais gauche d'une galit aprs sa
cration.
Cette surprenante proprit ne me semble pas clairement documente
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 inchange la fin des
oprations :

>>> =
== 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 proprit naturelle connue=

des listes, quelqu'un peut -il me dire comment faire pour conserver la
liste old sans modification pour pouvoir crer plusieurs listes
new1,new2,new3 en applicant des altrations diffrentes la mme=

base old

Merci d'avance qui pourra m'clairer
Cordialement

Michelc
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