Come copiare in profondità un elenco?


150

Ho qualche problema con una copia dell'elenco:

Così dopo ho ricevuto E0da 'get_edge', faccio una copia di E0chiamando 'E0_copy = list(E0)'. Qui immagino che E0_copysia una copia profonda di E0, e passo E0_copydentro 'karger(E)'. Ma nella funzione principale.
Perché il risultato 'print E0[1:10]'prima del ciclo for non è lo stesso di quello dopo il ciclo for?

Di seguito è il mio codice:

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
Inoltre, b = a [:] è una copia superficiale. Fare riferimento a stackoverflow.com/questions/16270374/…
Arvind Haran il

Risposte:


230

E0_copynon è una copia profonda. Non puoi fare una copia profonda usando list()(Entrambi list(...)e testList[:]sono copie superficiali).

Utilizzi copy.deepcopy(...)per copiare in profondità un elenco.

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

Vedi il seguente frammento:

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

Ora guarda l' deepcopyoperazione

>>> 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
Grazie, ma ho pensato che list () sia una copia profonda poiché id (E0) non è uguale a id (E0_copy). Potresti spiegare perché succede?
Shen,

15
list (...) non esegue in modo ricorsivo copie degli oggetti interni. Crea solo una copia dell'elenco più esterno, pur facendo riferimento agli elenchi interni della variabile precedente, quindi, quando mutate gli elenchi interni, la modifica si riflette sia nell'elenco originale che nella copia superficiale.
Sukrit Kalra,

1
Puoi vedere che la copia superficiale fa riferimento agli elenchi interni controllando quell'id (a [0]) == id (b [0]) dove b = list (a) e a è un elenco di elenchi.
Sukrit Kalra,

list1.append (list2) è anche una copia superficiale di list2
Lazik

60

Credo che molti programmatori abbiano riscontrato uno o due problemi di intervista in cui viene chiesto loro di copiare in profondità un elenco collegato, tuttavia questo problema è più difficile di quanto sembri!

in Python esiste un modulo chiamato "copia" con due utili funzioni

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

copy () è una funzione di copia superficiale, se l'argomento dato è una struttura di dati composta, ad esempio un elenco , allora Python creerà un altro oggetto dello stesso tipo (in questo caso, un nuovo elenco ) ma per tutto ciò che è all'interno del vecchio elenco, viene copiato solo il loro riferimento

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

Intuitivamente, potremmo supporre che deepcopy () segua lo stesso paradigma, e l'unica differenza è che per ogni elem chiameremo ricorsivamente deepcopy , (proprio come la risposta di mbcoder)

ma questo è sbagliato!

deepcopy () in realtà preserva la struttura grafica dei dati composti originali:

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

questa è la parte difficile, durante il processo di deepcopy () viene utilizzata una tabella hash (dizionario in python) per mappare: "ref vecchio_oggetto su nuovo riferimento_oggetto", questo impedisce duplicati non necessari e preserva così la struttura dei dati composti copiati

doc ufficiale


18

Se i contenuti dell'elenco sono tipi di dati primitivi, è possibile utilizzare una comprensione

new_list = [i for i in old_list]

Puoi nidificarlo per elenchi multidimensionali come:

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

5

Se il vostro list elementssono immutable objectsallora è possibile utilizzare questo, altrimenti è necessario utilizzare deepcopydal copymodulo.

puoi anche usare il modo più breve per copiare in profondità listcome questo.

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
Questa non è una copia profonda.
Sukrit Kalra,

1
Allora cos'è. Ha due dizionari diversi (puoi controllare gli ID di ognuno) con gli stessi valori.
tailor_raj,

Leggi questo , [:] crea solo una copia superficiale, non crea ricorsivamente copie degli oggetti all'interno di uno.
Sukrit Kalra,

1
Grazie. vuoi dire se usiamo questo, il nuovo elenco verrà creato ma tutti gli elementi del nuovo elenco saranno solo copie, avranno lo stesso oggetto (stesso id) del precedente?
tailor_raj,

Prova a utilizzare un elenco nidificato. Aggiorna l'elemento nidificato dell'elenco a. Verrà aggiornato anche nell'elenco b. Ciò implica che [:] non è una copia approfondita.
AnupamChugh

2

solo una funzione di copia profonda ricorsiva.

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

Modifica: come menzionato Cfreak, questo è già implementato nel copymodulo.


4
Non c'è motivo di reimplementare la deepcopy()funzione standard nel copymodulo
Cfreak,

1

Considerando la lista come un albero, deep_copy in Python può essere scritto in modo molto compatto come

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

0

Ecco un esempio di come copiare in profondità un elenco:

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

0

Questo è più 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: questo non è sicuro con un elenco di oggetti referenziati


2
Questo non funziona. Ho pensato che potrebbe ma solo un controllo. Prova con un elenco di dizionari come buon esempio
Shashank Singh,

@ShashankSingh sì, questo non funzionerà per un elenco di dizionari perché le voci sono tag di riferimento (che puntano a una posizione di memoria). Quindi la duplicazione di un elenco di dizionari con questo metodo creerà un nuovo elenco, ma poiché le voci sono dizionari faranno comunque riferimento alla stessa posizione di memoria.
Kwaw Annor
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.