¿Cómo hacer una copia completamente no compartida de una lista complicada? (Copia profunda no es suficiente)

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

  •  05-07-2019
  •  | 
  •  

Pregunta

Echa un vistazo a este código de 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]]]

Observe cómo la modificación de un elemento de c modifica eso en todas partes. Es decir, si 99 se anexa a c [0] [0] , también se anexa a c [1] [1] . Supongo que esto se debe a que Python hace referencia al mismo objeto para c [0] [0] y c [1] ] [1] . (Ese es su id () es el mismo.)

Pregunta: ¿Se puede hacer algo para c para que sus elementos de la lista se puedan modificar de forma local y segura? Lo anterior es solo un ejemplo, mi problema real tiene una lista mucho más complicada, pero tiene un problema similar.

(Perdón por la pregunta mal formada arriba. Los gurús de Python pueden modificar la pregunta o las etiquetas para expresar mejor esta consulta.)

¿Fue útil?

Solución

Para convertir una lista de listas existente en una en la que no se comparte nada, puede copiar la lista de forma recursiva.

deepcopy no será suficiente, ya que copiará la estructura como está, manteniendo las referencias internas como referencias, no como copias.

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

alist = unshared_copy(your_function_returning_lists())

Tenga en cuenta que esto supone que los datos se devuelven como una lista de listas (anidadas arbitrariamente). Si los contenedores son de diferentes tipos (por ejemplo, matrices numpy, dicts o clases de usuario), es posible que deba modificar esto.

Otros consejos

Cuando quiere una copia, hace una copia explícita: el [:] críptico " lo corta todo " la forma es idiomática, pero mi favorito es el enfoque mucho más legible de llamar explícitamente a list .

Si c se construye de la manera incorrecta (con referencias en lugar de copias superficiales a las listas que desea poder modificar de forma independiente), lo mejor sería corregir la forma en que se construyó (por qué compilar ¿está mal y luego se trabaja para solucionarlo ?!), pero si eso está fuera de su control, ES posible deshacer el daño; simplemente haga un bucle en c (recursivamente si es necesario), con un índice, reasignando el Listas relevantes a sus ejemplares. Por ejemplo, si está seguro de que la estructura de c es de dos niveles como usted indica, puede ahorrarse sin recursión:

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

A pesar de lo que sugieren otras respuestas, copy.deepcopy sería difícil de cumplir con este propósito peculiar, si todo lo que te dan es un c mal hecho: hazlo simplemente copy.deepcopy (c) replicaría cuidadosamente la topología de c, incluyendo múltiples referencias a las mismas sublistas. :-)

Dependiendo de su situación, es posible que desee trabajar contra una copia profunda de este lista.

Utilice [:] :

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

Alternativamente, use copy o deepcopy :

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

copy funciona en objetos que no sean listas. Para las listas, tiene el mismo efecto que a [:] . deepcopy intenta copiar de forma recursiva elementos anidados y, por lo tanto, es más exhaustivo " operación que copia .

Para ver la sugerencia de Stephan en el trabajo, compare los dos resultados a continuación:

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

La salida es la siguiente:

[[[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]]]
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top