Làm thế nào để sao chép sâu một danh sách?


148

Tôi có một số vấn đề với một bản sao Danh sách:

Vì vậy, Sau khi tôi nhận được E0từ 'get_edge', tôi thực hiện một bản sao của E0bằng cách gọi 'E0_copy = list(E0)'. Ở đây tôi đoán E0_copylà một bản sao sâu sắc E0, và tôi truyền E0_copyvào 'karger(E)'. Nhưng trong chức năng chính.
Tại sao kết quả 'print E0[1:10]'trước vòng lặp for không giống với kết quả sau vòng lặp for?

Dưới đây là mã của tôi:

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
Ngoài ra, b = a [:] là một bản sao nông. Tham khảo stackoverflow.com/questions/16270374/
Mạnh

Câu trả lời:


228

E0_copykhông phải là một bản sao sâu sắc. Bạn không tạo một bản sao sâu bằng cách sử dụng list()(Cả hai list(...)testList[:]là bản sao nông).

Bạn sử dụng copy.deepcopy(...)để sao chép sâu một danh sách.

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

Xem đoạn trích sau -

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

Bây giờ xem deepcopyhoạt động

>>> 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
Cảm ơn. Nhưng tôi nghĩ danh sách () là một bản sao sâu vì id (E0) không bằng id (E0_copy). Bạn có thể giải thích tại sao nó xảy ra?
Thần

14
list (...) không đệ quy tạo các bản sao của các đối tượng bên trong. Nó chỉ tạo một bản sao của danh sách ngoài cùng, trong khi vẫn tham chiếu các danh sách bên trong từ biến trước đó, do đó, khi bạn thay đổi danh sách bên trong, thay đổi được phản ánh trong cả danh sách gốc và bản sao nông.
Sukrit Kalra

1
Bạn có thể thấy rằng sao chép nông tham chiếu các danh sách bên trong bằng cách kiểm tra id đó (a [0]) == id (b [0]) trong đó b = list (a) và a là danh sách các danh sách.
Sukrit Kalra

list1.append (list2) cũng là một bản sao nông của list2
Lazik

60

Tôi tin rằng rất nhiều lập trình viên đã gặp phải một hoặc hai vấn đề phỏng vấn khi họ được yêu cầu sao chép sâu một danh sách liên kết, tuy nhiên vấn đề này khó hơn âm thanh!

trong python, có một mô-đun gọi là "sao chép" với hai chức năng hữu ích

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

copy () là một hàm sao chép nông, nếu đối số đã cho là cấu trúc dữ liệu hỗn hợp, ví dụ như một danh sách , thì python sẽ tạo một đối tượng khác cùng loại (trong trường hợp này là danh sách mới ) nhưng cho mọi thứ trong danh sách cũ, chỉ có tài liệu tham khảo của họ được sao chép

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

Theo trực giác, chúng ta có thể giả định rằng deepcopy () sẽ theo cùng một mô hình, và sự khác biệt duy nhất là với mỗi elem , chúng ta sẽ gọi đệ quy deepcopy , (giống như câu trả lời của mbcoder)

nhưng điều này là sai!

deepcopy () thực sự bảo tồn cấu trúc đồ họa của dữ liệu ghép gốc:

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

Đây là phần khó khăn, trong quá trình deepcopy (), một hashtable (từ điển trong python) được sử dụng để ánh xạ: "old_object ref vào new_object ref", điều này ngăn chặn các bản sao không cần thiết và do đó bảo tồn cấu trúc của dữ liệu ghép được sao chép

tài liệu chính thức


18

Nếu nội dung của danh sách là các kiểu dữ liệu nguyên thủy, bạn có thể sử dụng cách hiểu

new_list = [i for i in old_list]

Bạn có thể lồng nó cho các danh sách đa chiều như:

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

5

Nếu list elementslà của bạn immutable objectsthì bạn có thể sử dụng cái này, nếu không bạn phải sử dụng deepcopytừ copymodule.

bạn cũng có thể sử dụng cách ngắn nhất để sao chép sâu listnhư thế này.

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
Đây không phải là một bản sao sâu.
Sukrit Kalra

1
Thế nó là gì. Nó có hai từ điển khác nhau (bạn có thể kiểm tra id của từng loại) với cùng một giá trị.
tailor_raj

Đọc này , [:] chỉ tạo một bản sao nông, nó không tạo đệ quy các bản sao của các đối tượng bên trong một.
Sukrit Kalra

1
Cảm ơn. bạn có nghĩa là nếu chúng ta sử dụng danh sách này, danh sách mới sẽ được tạo nhưng tất cả các yếu tố của danh sách mới sẽ chỉ là bản sao, chúng sẽ có cùng một đối tượng (cùng id) như trước đây?
tailor_raj

Hãy thử sử dụng một danh sách lồng nhau. Cập nhật mục lồng nhau của danh sách a. Nó cũng sẽ được cập nhật trong danh sách b. Điều này ngụ ý một [:] không phải là bản sao sâu.
AnupamChugh

2

chỉ là một chức năng sao chép sâu đệ quy.

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

Chỉnh sửa: Như Cfreak đã đề cập, điều này đã được thực hiện trong copymô-đun.


4
Không có lý do để thực hiện lại deepcopy()chức năng tiêu chuẩn trong copymô-đun
Cfreak

1

Về danh sách dưới dạng cây, deep_copy trong python có thể được viết gọn nhất là

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

0

Đây là một ví dụ về cách sao chép sâu một danh sách:

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

0

Đây là nhiều 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.

LƯU Ý: Điều này không an toàn với danh sách các đối tượng được tham chiếu


2
Điều này không hoạt động. Tôi nghĩ rằng nó có thể nhưng chỉ cần kiểm tra. Hãy thử với một danh sách các từ điển như một ví dụ tốt
Shashank Singh

@ShashankSingh có, điều này sẽ không hoạt động cho một danh sách từ điển vì các mục nhập là các thẻ tham chiếu (trỏ đến một vị trí bộ nhớ). Vì vậy, sao chép một danh sách từ điển với phương thức này sẽ tạo ra một danh sách mới nhưng vì các mục là từ điển nên chúng vẫn sẽ tham chiếu cùng một vị trí bộ nhớ.
Kwaw Annor
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.