¿Cómo copiar en profundidad una lista?


150

Tengo algún problema con una copia de la lista:

Así Después de llegar E0a 'get_edge', hago una copia de E0llamando 'E0_copy = list(E0)'. Aquí supongo que E0_copyhay una copia profunda de E0, y paso E0_copya 'karger(E)'. Pero en la función principal.
¿Por qué el resultado de 'print E0[1:10]'antes del ciclo for no es el mismo que después del ciclo for?

Debajo está mi código:

def get_graph():
    f=open('kargerMinCut.txt')
    G={}
    for line in f:
        ints = [int(x) for x in line.split()]
        G[ints[0]]=ints[1:len(ints)]
    return G

def get_edge(G):
    E=[]
    for i in range(1,201):
        for v in G[i]:
            if v>i:
                E.append([i,v])
    print id(E)
    return E

def karger(E):
    import random
    count=200 
    while 1:
        if count == 2:
            break
        edge = random.randint(0,len(E)-1)
        v0=E[edge][0]
        v1=E[edge][1]                   
        E.pop(edge)
        if v0 != v1:
            count -= 1
            i=0
            while 1:
                if i == len(E):
                    break
                if E[i][0] == v1:
                    E[i][0] = v0
                if E[i][1] == v1:
                    E[i][1] = v0
                if E[i][0] == E[i][1]:
                    E.pop(i)
                    i-=1
                i+=1

    mincut=len(E)
    return mincut


if __name__=="__main__":
    import copy
    G = get_graph()
    results=[]
    E0 = get_edge(G)
    print E0[1:10]               ## this result is not equal to print2
    for k in range(1,5):
        E0_copy=list(E0)         ## I guess here E0_coypy is a deep copy of E0
        results.append(karger(E0_copy))
       #print "the result is %d" %min(results)
    print E0[1:10]               ## this is print2

2
Además, b = a [:] es una copia superficial. Consulte stackoverflow.com/questions/16270374/…
Arvind Haran el

Respuestas:


230

E0_copyNo es una copia profunda. No realiza una copia profunda utilizando list()(Ambos list(...)y testList[:]son copias superficiales).

Se utiliza copy.deepcopy(...)para copiar en profundidad una lista.

deepcopy(x, memo=None, _nil=[])
    Deep copy operation on arbitrary Python objects.

Ver el siguiente fragmento:

>>> a = [[1, 2, 3], [4, 5, 6]]
>>> b = list(a)
>>> a
[[1, 2, 3], [4, 5, 6]]
>>> b
[[1, 2, 3], [4, 5, 6]]
>>> a[0][1] = 10
>>> a
[[1, 10, 3], [4, 5, 6]]
>>> b   # b changes too -> Not a deepcopy.
[[1, 10, 3], [4, 5, 6]]

Ahora mira la deepcopyoperación

>>> import copy
>>> b = copy.deepcopy(a)
>>> a
[[1, 10, 3], [4, 5, 6]]
>>> b
[[1, 10, 3], [4, 5, 6]]
>>> a[0][1] = 9
>>> a
[[1, 9, 3], [4, 5, 6]]
>>> b    # b doesn't change -> Deep Copy
[[1, 10, 3], [4, 5, 6]]

3
Gracias, pero pensé que list () es una copia profunda ya que id (E0) no es igual a id (E0_copy). ¿Podría explicar por qué sucede?
Shen

15
list (...) no hace copias recursivas de los objetos internos. Solo hace una copia de la lista más externa, mientras hace referencia a las listas internas de la variable anterior, por lo tanto, cuando mutas las listas internas, el cambio se refleja tanto en la lista original como en la copia superficial.
Sukrit Kalra

1
Puede ver que la copia superficial hace referencia a las listas internas comprobando que id (a [0]) == id (b [0]) donde b = list (a) y a es una lista de listas.
Sukrit Kalra

list1.append (list2) también es una copia superficial de
list2

60

Creo que muchos programadores se han encontrado con uno o dos problemas de entrevista en los que se les pide que copien en profundidad una lista vinculada, sin embargo, ¡este problema es más difícil de lo que parece!

en python, hay un módulo llamado "copia" con dos funciones útiles

import copy
copy.copy()
copy.deepcopy()

copy () es una función de copia superficial, si el argumento dado es una estructura de datos compuesta, por ejemplo, una lista , entonces python creará otro objeto del mismo tipo (en este caso, una nueva lista ) pero para todo lo que esté dentro de la lista anterior, solo se copia su referencia

# think of it like
newList = [elem for elem in oldlist]

Intuitivamente, podríamos suponer que deepcopy () seguiría el mismo paradigma, y ​​la única diferencia es que para cada elemento llamaremos recursivamente deepcopy , (al igual que la respuesta de mbcoder)

pero esto esta mal!

deepcopy () en realidad preserva la estructura gráfica de los datos compuestos originales:

a = [1,2]
b = [a,a] # there's only 1 object a
c = deepcopy(b)

# check the result
c[0] is a # return False, a new object a' is created
c[0] is c[1] # return True, c is [a',a'] not [a',a'']

esta es la parte difícil, durante el proceso de deepcopy () se usa una tabla hash (diccionario en python) para asignar: "old_object ref en new_object ref", esto evita duplicados innecesarios y por lo tanto preserva la estructura de los datos compuestos copiados

documento oficial


18

Si el contenido de la lista son tipos de datos primitivos, puede usar una comprensión

new_list = [i for i in old_list]

Puede anidarlo para listas multidimensionales como:

new_grid = [[i for i in row] for row in grid]

5

Si list elementses immutable objectsasí, puede usar esto; de lo contrario, debe usarlo deepcopydesde el copymódulo.

También puede usar el camino más corto para una copia profunda listcomo esta.

a = [0,1,2,3,4,5,6,7,8,9,10]
b = a[:] #deep copying the list a and assigning it to b
print id(a)
20983280
print id(b)
12967208

a[2] = 20
print a
[0, 1, 20, 3, 4, 5, 6, 7, 8, 9,10]
print b
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10]

21
Esto no es una copia profunda.
Sukrit Kalra

1
Entonces que es eso. Tiene dos diccionarios diferentes (puede verificar las identificaciones de cada uno) con los mismos valores.
tailor_raj

Lea esto , [:] solo crea una copia superficial, no crea copias recursivas de los objetos dentro de uno.
Sukrit Kalra

1
Gracias. ¿quiere decir que si usamos esto, se creará una nueva lista pero todos los elementos de la nueva lista serán solo copias, tendrán el mismo objeto (mismo identificador) que el anterior?
tailor_raj

Intenta usar una lista anidada. Actualice el elemento anidado de la lista a. También se actualizará en la lista b. Esto implica que [:] no es una copia profunda.
AnupamChugh

2

solo una función recursiva de copia profunda.

def deepcopy(A):
    rt = []
    for elem in A:
        if isinstance(elem,list):
            rt.append(deepcopy(elem))
        else:
            rt.append(elem)
    return rt

Editar: como mencionó Cfreak, esto ya está implementado en el copymódulo.


44
No hay ninguna razón para deepcopy()copy
volver a implementar

1

En cuanto a la lista como un árbol, el deep_copy en python se puede escribir de forma más compacta como

def deep_copy(x):
    if not isinstance(x, list): return x
    else: return map(deep_copy, x)

0

Aquí hay un ejemplo de cómo copiar en profundidad una lista:

  b = [x[:] for x in a]

0

Esto es mas pitonico

my_list = [0, 1, 2, 3, 4, 5]  # some list
my_list_copy = list(my_list)  # my_list_copy and my_list does not share reference now.

NOTA: Esto no es seguro con una lista de objetos referenciados


2
Esto no funciona. Pensé que podría, pero solo lo comprobé. Pruebe con una lista de diccionarios como un buen ejemplo
Shashank Singh

@ShashankSingh sí, esto no funcionará para una lista de diccionarios porque las entradas son etiquetas de referencia (que apuntan a una ubicación de memoria). Por lo tanto, la duplicación de una lista de diccionario con este método creará una nueva lista, pero dado que las entradas son diccionarios, todavía harán referencia a la misma ubicación de memoria.
Kwaw Annor
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.