Como copiar uma lista em profundidade?

150

Estou com algum problema com uma cópia da lista:

Então Depois que eu comecei E0a partir 'get_edge', faço uma cópia E0chamando 'E0_copy = list(E0)'. Aqui eu acho que E0_copyé uma cópia profunda E0e eu passo E0_copyadiante 'karger(E)'. Mas na função principal.
Por que o resultado de 'print E0[1:10]'antes do loop for não é o mesmo que após o loop for?

Abaixo está o meu 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
Shen
fonte
2
Além disso, b = a [:] é uma cópia superficial. Consulte stackoverflow.com/questions/16270374/…
Arvind Haran

Respostas:

230

E0_copynão é uma cópia profunda. Você não faz uma cópia profunda usando list()(Ambas list(...)e testList[:]são cópias rasas).

Você usa copy.deepcopy(...)para copiar profundamente uma lista.

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

Veja o seguinte trecho -

>>> 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]]

Agora veja a deepcopyoperação

>>> 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]]
Sukrit Kalra
fonte
3
Mas acho que list () é uma cópia profunda, pois o id (E0) não é igual ao id (E0_copy). Você poderia explicar por que isso aconteceu?
Shen
15
list (...) não faz cópias recursivas dos objetos internos. Ele faz apenas uma cópia da lista mais externa, enquanto ainda faz referência às listas internas da variável anterior; portanto, quando você modifica as listas internas, a alteração é refletida na lista original e na cópia superficial.
Sukrit Kalra
1
Você pode ver que a cópia superficial faz referência às listas internas verificando aquele id (a [0]) == id (b [0]) em que b = list (a) e a são uma lista de listas.
Sukrit Kalra
list1.append (list2) também é uma cópia superficial da list2
Lazik 12/12
60

Acredito que muitos programadores se depararam com um ou dois problemas de entrevistas nos quais são solicitados a copiar em profundidade uma lista vinculada, mas esse problema é mais difícil do que parece!

em python, existe um módulo chamado "copy" com duas funções úteis

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

copy () é uma função superficial de cópia, se o argumento fornecido for uma estrutura de dados composta, por exemplo, uma lista , o python criará outro objeto do mesmo tipo (neste caso, uma nova lista ), mas para tudo dentro da lista antiga, somente sua referência é copiada

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

Intuitivamente, poderíamos assumir que deepcopy () seguiria o mesmo paradigma, e a única diferença é que, para cada elem, chamaremos recursivamente deepcopy (assim como a resposta do mbcoder)

mas isso está errado!

deepcopy () preserva a estrutura gráfica dos dados compostos originais:

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 é a parte complicada, durante o processo de deepcopy (), uma hashtable (dicionário em python) é usada para mapear: "old_object ref into new_object ref", isso evita duplicatas desnecessárias e preserva a estrutura dos dados compostos copiados

doc oficial

watashiSHUN
fonte
18

Se o conteúdo da lista for tipos de dados primitivos, você poderá usar uma compreensão

new_list = [i for i in old_list]

Você pode aninhar para listas multidimensionais como:

new_grid = [[i for i in row] for row in grid]
aljgom
fonte
5

Se o seu list elementssão immutable objects, em seguida, você pode usar este, caso contrário, você tem que usar deepcopya partir copydo módulo.

você também pode usar o caminho mais curto para cópias profundas, 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]
tailor_raj
fonte
21
Esta não é uma cópia profunda.
Sukrit Kalra
1
Então, o que é. Possui dois dicionários diferentes (você pode verificar os IDs de cada um) com os mesmos valores.
Tailor_raj 26/07/2013
Leia isto , [:] apenas cria uma cópia superficial, não cria recursivamente cópias dos objetos dentro de uma.
Sukrit Kalra
1
Obrigado. você quer dizer que se usarmos isso, uma nova lista será criada, mas todos os elementos da nova lista serão apenas cópias, eles terão o mesmo objeto (o mesmo ID) que o anterior?
Tailor_raj 26/07/2013
Tente usar uma lista aninhada. Atualize o item aninhado da lista a. Também será atualizado na lista b. Isso implica que uma [:] não é uma cópia profunda.
AnupamChugh 10/04
2

apenas uma função de cópia profunda recursiva.

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

Edit: Como o Cfreak mencionou, isso já está implementado no copymódulo.

rnbguy
fonte
4
Não há nenhuma razão para reimplementar o padrão deepcopy()função no copymódulo
Cfreak
1

Em relação à lista como uma árvore, o deep_copy em python pode ser escrito de forma mais compacta como

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

Aqui está um exemplo de como copiar uma lista em profundidade:

  b = [x[:] for x in a]
AnupamChugh
fonte
0

Isso é mais pitônico

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: Isso não é seguro com uma lista de objetos referenciados

Kwaw Annor
fonte
2
Isso não funciona. Eu pensei que poderia, mas a apenas verificado. Tente com uma lista de dicionários como um bom exemplo
Shashank Singh
@ShashankSingh sim, isso não funcionará para uma lista de dicionários porque as entradas são marcas de referência (apontando para um local de memória). Portanto, duplicar uma lista de dicionário com esse método criará uma nova lista, mas como as entradas são dicionários, elas ainda farão referência ao mesmo local de memória.
Kwaw Annor