วิธีคัดลอกรายการลึก?


150

ฉันมีปัญหากับการคัดลอกรายการ:

ดังนั้นหลังจากที่ผมได้รับE0จาก'get_edge'ผมทำสำเนาโดยการเรียกE0 'E0_copy = list(E0)'นี่ฉันเดาE0_copyเป็นสำเนาลึกE0และผมผ่านเข้าไปE0_copy 'karger(E)'แต่ในฟังก์ชั่นหลัก
ทำไมผลลัพธ์ของ'print E0[1:10]'for for loop จึงไม่เหมือนกันหลังจากนั้น for for loop

ด้านล่างเป็นรหัสของฉัน:

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
นอกจากนี้ b = a [:] เป็นสำเนาตื้น อ้างถึงstackoverflow.com/questions/16270374/…
Arvind Haran

คำตอบ:


231

E0_copyไม่ใช่สำเนาที่ลึกซึ้ง คุณไม่ได้ทำสำเนาลึกโดยใช้list()(ทั้งสองlist(...)และtestList[:]เป็นสำเนาตื้น)

คุณใช้copy.deepcopy(...)สำหรับการคัดลอกรายการลึก

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

ดูตัวอย่างต่อไปนี้ -

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

ตอนนี้ดูการdeepcopyดำเนินการ

>>> 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
Thanks.But ฉันคิดว่า list () เป็นสำเนาที่ลึกตั้งแต่ id (E0) ไม่เท่ากับ id (E0_copy) คุณสามารถอธิบายได้ว่าทำไมมันเกิดขึ้น?
Shen

15
รายการ (... ) ไม่ได้ทำสำเนาของวัตถุภายในซ้ำ มันทำสำเนาของรายการนอกสุดเท่านั้นในขณะที่ยังคงอ้างอิงรายการภายในจากตัวแปรก่อนหน้าดังนั้นเมื่อคุณกลายพันธุ์รายการภายในการเปลี่ยนแปลงจะแสดงทั้งในรายการดั้งเดิมและสำเนาตื้น
Sukrit Kalra

1
คุณจะเห็นว่าการคัดลอกตื้นอ้างอิงรายการภายในโดยตรวจสอบ id ที่ (a [0]) == id (b [0]) โดยที่ b = list (a) และ a เป็นรายการของรายการ
Sukrit Kalra

list1.append (list2) เป็นสำเนาตื้น ๆ ของ list2
Lazik

60

ฉันเชื่อว่าโปรแกรมเมอร์จำนวนมากพบปัญหาการสัมภาษณ์หนึ่งหรือสองครั้งที่พวกเขาถูกขอให้คัดลอกรายการที่ลิงก์อย่างลึกล้ำอย่างไรก็ตามปัญหานี้ยากกว่าที่คิด!

ในหลามมีโมดูลที่เรียกว่า "คัดลอก" ที่มีสองฟังก์ชั่นที่มีประโยชน์

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

คัดลอก () เป็นฟังก์ชั่นการคัดลอกตื้นถ้าอาร์กิวเมนต์ที่กำหนดเป็นโครงสร้างข้อมูลแบบผสมเช่นรายการจากนั้นไพ ธ อนจะสร้างวัตถุชนิดเดียวกันอีกอันหนึ่ง (ในกรณีนี้คือรายการใหม่ ) แต่สำหรับทุกสิ่งในรายการเก่า เฉพาะการอ้างอิงของพวกเขาถูกคัดลอก

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

โดยสัญชาตญาณเราสามารถสรุปได้ว่า Deepcopy () จะทำตามกระบวนทัศน์เดียวกันและความแตกต่างเพียงอย่างเดียวคือสำหรับแต่ละชนชั้นที่เราเรียก recursively จะเรียกว่า deepcopy (เหมือนกับคำตอบของ mbcoder)

แต่นี่มันผิด!

Deepcopy () รักษาโครงสร้างกราฟิกของข้อมูลสารประกอบดั้งเดิม:

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

นี่คือส่วนที่ยุ่งยากในระหว่างกระบวนการของ deepcopy () hashtable (พจนานุกรมในภาษาไพ ธ อน) ใช้ในการทำแผนที่: "old_object ref ไปยัง new_object ref" ซึ่งจะป้องกันการซ้ำซ้อนที่ไม่จำเป็นและรักษาโครงสร้างของข้อมูลสารประกอบที่คัดลอก

doc อย่างเป็นทางการ


18

หากเนื้อหาของรายการเป็นชนิดข้อมูลดั้งเดิมคุณสามารถใช้ความเข้าใจ

new_list = [i for i in old_list]

คุณสามารถซ้อนมันสำหรับรายการหลายมิติเช่น:

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

5

หากคุณlist elementsเป็นเช่นimmutable objectsนั้นคุณสามารถใช้สิ่งนี้มิฉะนั้นคุณต้องใช้deepcopyจากcopyโมดูล

คุณยังสามารถใช้วิธีที่สั้นที่สุดเพื่อทำสำเนาlistอย่างนี้

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
นี่ไม่ใช่การถ่ายเอกสารแบบลึก
Sukrit Kalra

1
แล้วมันคืออะไร มันมีสองพจนานุกรมที่แตกต่างกัน (คุณสามารถตรวจสอบ id ของแต่ละคน) ด้วยค่าเดียวกัน
tailor_raj

อ่านสิ่งนี้ , [:] เพียงแค่สร้างสำเนาตื้น ๆ , มันไม่ได้สร้างสำเนาของวัตถุซ้ำภายในหนึ่งครั้ง
Sukrit Kalra

1
ขอบคุณ คุณหมายถึงว่าถ้าเราใช้สิ่งนี้รายการใหม่จะถูกสร้างขึ้น แต่องค์ประกอบทั้งหมดของรายการใหม่จะถูกคัดลอกเท่านั้นพวกเขาจะมีวัตถุเดียวกัน (id เดียวกัน) จากรายการก่อนหน้าใช่หรือไม่
tailor_raj

ลองใช้รายการซ้อนกัน อัปเดตรายการซ้อนในรายการ มันจะได้รับการอัพเดตในรายการ b ด้วย นี่หมายถึง [:] ไม่ใช่สำเนาที่ลึก
AnupamChugh

2

เพียงฟังก์ชั่นการคัดลอกซ้ำลึก

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

แก้ไข: ดังที่ Cfreak พูดถึงสิ่งนี้ได้ถูกนำไปใช้ในcopyโมดูลแล้ว


4
ไม่มีเหตุผลที่จะปรับใช้deepcopy()ฟังก์ชันมาตรฐานในcopyโมดูลอีกครั้ง
Cfreak

1

เกี่ยวกับรายการเป็นต้นไม้ deep_copy ในไพ ธ อนสามารถเขียนเป็นดานส่วนใหญ่เป็น

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


0

นี่คือ 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.

หมายเหตุ: นี่ไม่ปลอดภัยกับรายการของวัตถุที่อ้างอิง


2
สิ่งนี้ใช้ไม่ได้ ฉันคิดว่ามันอาจจะเป็นแค่การตรวจสอบ ลองใช้รายการพจนานุกรมเป็นตัวอย่างที่ดี
Shashank Singh

@ShashankSingh ใช่สิ่งนี้จะไม่ทำงานกับรายการพจนานุกรมเพราะรายการเป็นแท็กอ้างอิง (ชี้ไปที่ตำแหน่งหน่วยความจำ) ดังนั้นการทำสำเนารายการพจนานุกรมด้วยวิธีนี้จะสร้างรายการใหม่ แต่เนื่องจากรายการเหล่านั้นเป็นพจนานุกรมพวกเขาจะยังคงอ้างอิงตำแหน่งหน่วยความจำเดียวกัน
Kwaw Annor
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.