2009-10-21 23 views
6

Tener un vistazo a este código Python:¿Cómo hacer una copia completamente no compartida de una lista complicada? (Copia en profundidad no es suficiente)

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 modificar un elemento de c modifica que en todas partes. Es decir, si se agrega 99 al c[0][0], también se agrega al c[1][1]. Supongo que esto se debe a que Python está hábilmente refiriéndose al mismo objeto para c[0][0] y c[1][1]. (Esa es su id() es lo mismo).

Pregunta: ¿Hay algo que se pueda hacer a c de manera que sus elementos de la lista pueden ser modificados de manera segura a nivel local? Más arriba es solo un ejemplo, mi problema real tiene una lista mucho más complicada, pero tiene un problema similar.

(.. Lo siento por la pregunta mal formada por encima de los gurús de Python favor no dude en modificar la pregunta o etiquetas para expresar mejor esta consulta)

+4

Cuando llegue media hora, es posible que desee estudiar Python's Data Model: http://docs.python.org/reference/datamodel.html – Triptych

+0

Tríptico: Gracias por la sugerencia. Lo haré este fin de semana :-) –

Respuesta

8

Para convertir una lista existente de listas a una donde no se comparte nada, puede copiar recursivamente la lista.

deepcopy no será suficiente, ya que copiará la estructura como está, manteniendo 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()) 

Se ha tomado los datos se devuelven como una lista de listas (arbitrariamente anidados). Si los contenedores son de diferentes tipos (por ejemplo, matrices numpy, dicts o clases de usuario), es posible que deba modificar esto.

+0

Gracias Brian! ¡Su intuición acerca de que la copia profunda no funcionaba era la correcta! Tu respuesta resolvió mi problema real. También modifiqué su código para Python 3, donde map() devuelve un objeto de mapa, no una lista. –

5

Uso [:]:

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

Alteratively, utilice copy or 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 listas, tiene el mismo efecto que a[:]. deepcopy intenta copiar recursivamente los elementos anidados, y por lo tanto es una operación más "completa" que copy.

+1

Gracias Stephan202. Su solución funciona solo para listas muy simples y su construcción. Mi lista real es mucho más complicada en la construcción y lo que resolvió mi problema al final fue la respuesta de Brian de una copia no compartida recursiva de la lista. –

5

Dependiendo de su situación, es posible que desee trabajar contra un deep copy de esta lista.

+1

Gracias por la sugerencia. Pero, tanto la copia (superficial) como la copia profunda no funcionaron para mi problema real. Tuve que recurrir a la solución de Brian. –

8

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

Si c se construye de forma incorrecta (con referencias en lugar de copias superficiales en listas que desea modificar independientemente) lo mejor sería corregir la forma en que está construido (por qué construirlo incorrectamente y luego trabajar para arreglarlo ?!), pero si eso está fuera de tu control, ES POSIBLE deshacer el daño - solo haz un bucle en c (recursivamente si es necesario), con un índice, reasignando las sublistas relevantes a sus copias. Por ejemplo, si se sabe con certeza que la estructura c 's es de dos niveles como usted indica, se puede ahorrar sin recursividad:

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

A pesar de lo que otras respuestas sugieren, copy.deepcopy sería difícil de doblar a esta peculiar Propósito, si todo lo que se le da es incorrectamente c: haciendo solo copy.deepcopy(c) se replicará cuidadosamente cualquier topología de c, ¡incluyendo múltiples referencias a las mismas sublistas!:-)

+0

Gracias Alex. De hecho, mi lista real es más complicada (en construcción) y la copia en profundidad no resolvió el problema. Usé una copia no compartida recursiva (la respuesta de Brian) para resolver el problema (ya que la lista era de 3 niveles). –

1

Para ver la sugerencia de Stephan en el trabajo, comparar las dos salidas 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]]] 
+1

Michael: Recuerda que en este caso obtengo c de una función sobre la que no tengo control. Si le acaban de entregar c, que podría tener muchas de esas listas que se refieren al mismo objeto, ¿cómo lo trataría? –

Cuestiones relacionadas