Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

une surprise en modifiant des listes

1 réponse
Avatar
michelc
Bonjour,

commen=E7ant de bidouiller depuis quelques semaines avec Python 2.5, je
suis tomb=E9 sur la surprise suivante.

>>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D RESTART =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
>>> old =3D ['abcdef\n','ghijkl\n']
>>> new =3D old
>>> old
['abcdef\n', 'ghijkl\n']
>>> new
['abcdef\n', 'ghijkl\n']
>>> new[1]=3D new[1][0:2] +'zz'+new[1][4:]
>>> new
['abcdef\n', 'ghzzkl\n']
>>> old
['abcdef\n', 'ghzzkl\n']
>>>

Autrement dit la modification d'un =E9l=E9ment de la liste new entraine la
modification =E0 l'identique de la liste old
bien que old n'apparaisse jamais =E0 gauche d'une =E9galit=E9 apr=E8s sa
cr=E9ation.
Cette surprenante propri=E9t=E9 ne me semble pas clairement document=E9e
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=E9e =E0 la fin des
op=E9rations :

>>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D RESTART =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
>>> old =3D 'abcdef\n'
>>> new =3D old
>>> old
'abcdef\n'
>>> new
'abcdef\n'
>>> new=3D new[0:2] +'zz'+new[4:]
>>> new
'abzzef\n'
>>> old
'abcdef\n'
>>>

Si, comme je le suppose, il s'agit l=E0 d'une propri=E9t=E9 naturelle connue=

des listes, quelqu'un peut -il me dire comment faire pour conserver la
liste old sans modification pour pouvoir cr=E9er plusieurs listes
new1,new2,new3... en applicant des alt=E9rations diff=E9rentes =E0 la m=EAme=

base old

Merci d'avance =E0 qui pourra m'=E9clairer
Cordialement

Michelc

1 réponse

Avatar
Bruno Desthuilliers
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