如何深层复制列表?


149

我对列表副本有一些问题:

所以之后我得到了E0来自'get_edge',我做的副本E0通过调用'E0_copy = list(E0)'。我想E0_copy是这里的深复制E0,我E0_copy走进了'karger(E)'。但是在主要功能上。
为什么'print E0[1:10]'for循环之前的结果与for循环之后的结果不同?

下面是我的代码:

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/...
阿文德哈兰

Answers:


229

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
谢谢。但是我认为list()是一个深层副本,因为id(E0)不等于id(E0_copy)。你能解释为什么会这样吗?
2013年

14
list(...)不会递归地复制内部对象。它仅制作最外层列表的副本,同时仍引用前一个变量的内层列表,因此,当您对内层列表进行突变时,更改将同时反映在原始列表和浅表副本中。
Sukrit Kalra

1
您可以通过检查id(a [0])== id(b [0])来看到浅表复制引用了内部列表,其中b = list(a),a是列表列表。
Sukrit Kalra

list1.append(list2)也是list2的浅表副本
Lazik

60

我相信很多程序员遇到一个或两个面试问题,他们被要求深度复制链表,但是这个问题比听起来要难!

在python中,有一个名为“ copy”的模块,具有两个有用的功能

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

如果给定参数是复合数据结构,例如,则copy()是浅表复制函数。 list,则python将创建另一个相同类型的对象(在这种情况下为new list),但对于旧列表中的所有对象,仅复制他们的参考

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

直观地,我们可以假定deepcopy()遵循相同的范例,唯一的区别是对于每个元素,我们将递归调用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()过程中,哈希表(python中的字典)用于映射:“ old_object ref到new_object ref”,这可以防止不必要的重复,从而保留复制的复合数据的结构

官方文件


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则可以使用它,否则必须使用deepcopyfrom 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)。
裁缝_raj 2013年

阅读此内容,[:]只会创建一个浅表副本,不会递归地创建其中的对象副本。
Sukrit Kalra

1
谢谢。您的意思是说,如果使用此方法,将创建新列表,但新列表的所有元素将仅是副本,它们将具有与上一个相同的对象(相同的ID)?
裁缝_raj

尝试使用嵌套列表。更新列表的嵌套项目a。它也会在列表b中更新。这意味着a [:]不是深层副本。
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

关于列表作为树,Python中的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是的,这对词典列表不起作用,因为条目是参考标记(指向存储位置)。因此,使用此方法复制字典列表将创建一个新列表,但是由于条目是字典,因此它们仍将引用相同的存储位置。
夸·安诺
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.