Bagaimana cara menyalin daftar?

150

Saya memiliki beberapa masalah dengan salinan Daftar:

Jadi Setelah aku E0dari 'get_edge', saya membuat salinan E0dengan menelepon 'E0_copy = list(E0)'. Di sini saya kira E0_copyadalah salinan yang dalam E0, dan saya E0_copymasuki 'karger(E)'. Namun dalam fungsi utama.
Mengapa hasil 'print E0[1:10]'sebelum for for tidak sama dengan setelah for for?

Di bawah ini adalah kode saya:

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
sumber
2
Juga, b = a [:] adalah salinan dangkal. Lihat stackoverflow.com/questions/16270374/…
Arvind Haran

Jawaban:

230

E0_copybukan salinan yang dalam. Anda tidak membuat salinan dalam menggunakan list()(Keduanya list(...)dan testList[:]merupakan salinan dangkal).

Anda gunakan copy.deepcopy(...)untuk menyalin daftar secara mendalam.

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

Lihat cuplikan berikut -

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

Sekarang lihat deepcopyoperasinya

>>> 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
sumber
3
Terima kasih. Tetapi saya pikir daftar () adalah salinan yang dalam karena id (E0) tidak sama dengan id (E0_copy). Bisakah kamu menjelaskan mengapa itu terjadi?
Shen
15
daftar (...) tidak secara rekursif membuat salinan dari objek dalam. Itu hanya membuat salinan daftar terluar, sementara masih merujuk daftar dalam dari variabel sebelumnya, karenanya, ketika Anda mengubah daftar dalam, perubahan tercermin dalam daftar asli dan salinan dangkal.
Sukrit Kalra
1
Anda dapat melihat bahwa referensi penyalinan dangkal daftar dalam dengan memeriksa id itu (a [0]) == id (b [0]) di mana b = daftar (a) dan a adalah daftar daftar.
Sukrit Kalra
list1.append (list2) juga merupakan salinan list2 yang dangkal
Lazik
60

Saya percaya banyak programmer telah mengalami satu atau dua masalah wawancara di mana mereka diminta untuk menyalin daftar terkait, tetapi masalah ini lebih sulit daripada kedengarannya!

dalam python, ada modul yang disebut "copy" dengan dua fungsi yang bermanfaat

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

copy () adalah fungsi salin dangkal, jika argumen yang diberikan adalah struktur data majemuk, misalnya daftar , maka python akan membuat objek lain dengan tipe yang sama (dalam hal ini, daftar baru ) tetapi untuk semua yang ada di dalam daftar lama, hanya referensi mereka yang disalin

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

Secara intuitif, kita dapat mengasumsikan bahwa deepcopy () akan mengikuti paradigma yang sama, dan satu-satunya perbedaan adalah bahwa untuk setiap elem kita akan secara rekursif memanggil deepcopy , (seperti jawaban dari mbcoder)

tapi ini salah!

deepcopy () sebenarnya mempertahankan struktur grafis dari data senyawa asli:

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

ini adalah bagian yang sulit, selama proses deepcopy () hashtable (kamus dengan python) digunakan untuk memetakan: "old_object ref ke new_object ref", ini mencegah duplikat yang tidak perlu dan dengan demikian mempertahankan struktur data gabungan yang disalin

dokumen resmi

watashiSHUN
sumber
18

Jika konten daftar adalah tipe data primitif, Anda dapat menggunakan pemahaman

new_list = [i for i in old_list]

Anda dapat membuat sarang untuk daftar multidimensi seperti:

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

Jika Anda list elementsadalah immutable objectsmaka Anda dapat menggunakan ini, jika tidak, anda harus menggunakan deepcopydari copymodul.

Anda juga dapat menggunakan cara terpendek untuk menyalin dalam listseperti ini.

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
sumber
21
Ini bukan salinan yang dalam.
Sukrit Kalra
1
Lalu apa itu. Ini memiliki dua kamus yang berbeda (Anda dapat memeriksa id masing-masing) dengan nilai yang sama.
tailor_raj
Baca ini , [:] hanya membuat salinan dangkal, itu tidak secara rekursif membuat salinan objek di dalam satu.
Sukrit Kalra
1
Terima kasih. Anda bermaksud mengatakan jika kita menggunakan ini, daftar baru akan dibuat tetapi semua elemen dari daftar baru akan hanya salinan, mereka akan memiliki objek yang sama (id yang sama) seperti yang sebelumnya?
tailor_raj
Coba gunakan daftar bersarang. Perbarui item bersarang dari daftar a. Itu akan diperbarui dalam daftar b juga. Ini menyiratkan [:] bukan salinan dalam.
AnupamChugh
2

hanya fungsi salin dalam rekursif.

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

Sunting: Seperti yang disebutkan Cfreak, ini sudah diimplementasikan dalam copymodul.

rnbguy
sumber
4
Tidak ada alasan untuk mengimplementasikan kembali deepcopy()fungsi standar dalam copymodul
Cfreak
1

Mengenai daftar sebagai pohon, deep_copy dalam python dapat ditulis paling ringkas

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

Berikut adalah contoh cara menyalin daftar:

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

Ini lebih pythonic

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.

CATATAN: Ini tidak aman dengan daftar objek yang dirujuk

Kwaw Annor
sumber
2
Ini tidak bekerja. Saya pikir itu mungkin tetapi hanya diperiksa. Coba dengan daftar kamus sebagai contoh yang baik
Shashank Singh
@ShashankSingh ya ini tidak akan berfungsi untuk daftar kamus karena entri adalah tag referensi (menunjuk ke lokasi memori). Jadi duplikasi daftar kamus dengan metode ini akan membuat daftar baru tetapi karena entri adalah kamus mereka masih akan merujuk ke lokasi memori yang sama.
Kwaw Annor