Comment faire une copie complètement non partagée d'une liste compliquée? (Copie profonde n'est pas suffisant)

StackOverflow https://stackoverflow.com/questions/1601269

  •  05-07-2019
  •  | 
  •  

Question

Jetez un coup d'œil à ce code Python:

a = [1, 2, 3]
b = [4, 5, 6]
c = [[a, b], [b, a]] # [[[1, 2, 3], [4, 5, 6]], [[4, 5, 6], [1, 2, 3]]]
c[0][0].append(99)   # [[[1, 2, 3, 99], [4, 5, 6]], [[4, 5, 6], [1, 2, 3, 99]]]

Remarquez comment la modification d’un élément de c le modifie partout. En d’autres termes, si 99 est ajouté à c [0] [0] , il est également ajouté à c [1] [1] . J'imagine que c'est parce que Python fait intelligemment référence au même objet pour c [0] [0] et c [1 ] [1] . (C’est leur id () qui est identique.)

Question: Peut-on faire quelque chose pour c afin que ses éléments de liste puissent être modifiés localement en toute sécurité? Ci-dessus n’est qu’un exemple, mon vrai problème a une liste beaucoup plus compliquée, mais ayant un problème similaire.

(Désolé pour la question mal formulée ci-dessus. Gourous de Python, n'hésitez pas à modifier la question ou les balises pour mieux exprimer cette requête.)

Était-ce utile?

La solution

Pour convertir une liste de listes existante en une liste dans laquelle rien n'est partagé, vous pouvez copier la liste de manière récursive.

deepcopy ne sera pas suffisant, car il copiera la structure telle quelle, en conservant les références internes comme références et non comme des copies.

def unshared_copy(inList):
    if isinstance(inList, list):
        return list( map(unshared_copy, inList) )
    return inList

alist = unshared_copy(your_function_returning_lists())

Notez que cela suppose que les données sont renvoyées sous forme de liste de listes (imbriquées de manière arbitraire). Si les conteneurs sont de types différents (par exemple, tableaux numpy, tableaux ou classes d’utilisateurs), vous devrez peut-être modifier cela.

Autres conseils

Lorsque vous souhaitez une copie, vous en faites explicitement une copie - le [:] crypté "slice it all all" " la forme est idiomatique, mais mon préféré est l’approche beaucoup plus lisible d’appeler explicitement la liste .

Si c est construit de manière incorrecte (avec des références plutôt que des copies superficielles à des listes que vous voulez pouvoir modifier indépendamment), le mieux serait de corriger le mode de construction (pourquoi mal, et puis il faut travailler pour le réparer?!), mais si cela échappe à votre contrôle, il est possible de réparer les dommages - il suffit de boucler sur c (récursivement si nécessaire), avec un index, en réaffectant le sous-listes pertinentes à leurs copies. Par exemple, si vous savez avec certitude que la structure de c est à deux niveaux comme vous l'indiquez, vous pouvez vous enregistrer sans récursivité:

def fixthewronglymadelist(c):
  for topsublist in c:
    for i, L in enumerate(topsublist):
      topsublist[i] = list(L)

En dépit de ce que suggèrent d’autres réponses, il serait difficile de copy.deepcopy de se plier à cet objectif particulier, si tout ce que l’on vous donne est le c mal fait copy.deepcopy (c) serait soigneusement reproduit quelle que soit la topologie de c, y compris plusieurs références aux mêmes sous-listes! : -)

Selon votre situation, vous pouvez utiliser une copie en profondeur de cette liste.

Utilisez [:] :

>>> a = [1, 2]
>>> b = a[:]
>>> b.append(9)
>>> a
[1, 2]

Sinon, utilisez copier ou deepcopy :

>>> import copy
>>> a = [1, 2]
>>> b = copy.copy(a)
>>> b.append(9)
>>> a
[1, 2]

copy fonctionne sur des objets autres que des listes. Pour les listes, cela a le même effet que a [:] . deepcopy tente de copier de manière récursive des éléments imbriqués. Il s'agit donc d'un document plus "exhaustif". opération qui copie .

Pour voir la suggestion de Stephan à l'œuvre, comparez les deux résultats ci-dessous:

a = [1, 2, 3]
b = [4, 5, 6]
c = [[a, b], [b, a]]
c[0][0].append(99)
print c
print "-------------------"
a = [1, 2, 3]
b = [4, 5, 6]
c = [[a[:], b[:]], [b[:], a[:]]]
c[0][0].append(99)
print c

Le résultat est le suivant:

[[[1, 2, 3, 99], [4, 5, 6]], [[4, 5, 6], [1, 2, 3, 99]]]
-------------------
[[[1, 2, 3, 99], [4, 5, 6]], [[4, 5, 6], [1, 2, 3]]]
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top