221

之后E0_copy = list(E0),我猜E0_copyE0因为id(E0)不等于深拷贝id(E0_copy)。然后我E0_copy在循环中修改,为什么E0之后不一样了?

E0 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for k in range(3):
    E0_copy = list(E0)
    E0_copy[k][k] = 0
    #print(E0_copy)
print E0  # -> [[0, 2, 3], [4, 0, 6], [7, 8, 0]]
4

10 回答 10

320

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

解释一下,list(...)不会递归地复制内部对象。它只制作最外层列表的副本,同时仍然引用相同的内部列表,因此,当您改变内部列表时,更改会反映在原始列表和浅表副本中。id(a[0]) == id(b[0])您可以通过检查where看到浅复制引用了内部列表b = list(a)

于 2013-07-26T05:13:22.267 回答
85

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

在 Python 中,有一个模块调用copy了两个有用的函数:

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

copy()是一个浅拷贝函数。如果给定的参数是一个复合数据结构,例如list,那么 Python 将创建另一个相同类型的对象(在这种情况下,一个新的 list),但对于旧列表中的所有内容,只复制它们的引用。想一想:

newList = [elem for elem in oldlist]

直观地说,我们可以假设这deepcopy()将遵循相同的范例,唯一的区别是对于每个elem,我们将递归调用 deepcopy,(就像mbguy 的回答一样)

但这是错误的!

deepcopy()实际上保留了原始复合数据的图形结构:

a = [1,2]
b = [a,a] # there's only 1 object a
c = deepcopy(b)

# check the result
c[0] is a # False, a new object a_1 is created
c[0] is c[1] # True, c is [a_1, a_1] not [a_1, a_2]

这是棘手的部分:在 的过程中deepcopy(),使用哈希表(Python 中的字典)将每个旧对象 ref 映射到每个新对象 ref,这可以防止不必要的重复,从而保留复制的复合数据的结构。

官方文档

于 2015-09-25T22:33:52.020 回答
26

如果列表的内容是原始数据类型,则可以使用推导

new_list = [i for i in old_list]

您可以将其嵌套在多维列表中,例如:

new_grid = [[i for i in row] for row in grid]
于 2017-04-16T13:49:31.617 回答
8

如果您的列表元素是不可变对象,那么您可以使用它,否则您必须使用deepcopyfrom copymodule.

您也可以使用最短的方式进行深度复制,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]
于 2013-07-26T06:22:24.740 回答
3

@苏克里特卡拉

No.1: list(), [:],copy.copy()都是浅拷贝。如果一个对象是复合的,它们都不合适。你需要使用copy.deepcopy().

No.2:b = a直接,a并且b具有相同的参考意义,改变a等于改变b

将 a 设置为 b

如果直接分配abab分享一个参考。

>>> a = [[1, 2, 3], [4, 5, 6]]
>>> b = a
>>> a
[[1, 2, 3], [4, 5, 6]]
>>> b
[[1, 2, 3], [4, 5, 6]]
>>> a[0] = 1
>>> a
[1, [4, 5, 6]]
>>> b
[1, [4, 5, 6]]


>>> a = [[1, 2, 3], [4, 5, 6]]
>>> b = 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
[[1, 10, 3], [4, 5, 6]]

影子副本

经过list()

list()并且[:]是相同的。除了第一层的变化外,所有其他层的变化都会被转移。

>>> 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
>>> a
[1, [4, 5, 6]]
>>> b
[[1, 2, 3], [4, 5, 6]]


>>> 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
[[1, 10, 3], [4, 5, 6]]

经过[:]

>>> a = [[1, 2, 3], [4, 5, 6]]
>>> b = a[:]
>>> a
[[1, 2, 3], [4, 5, 6]]
>>> b
[[1, 2, 3], [4, 5, 6]]
>>> a[0] = 1
>>> a
[1, [4, 5, 6]]
>>> b
[[1, 2, 3], [4, 5, 6]]


>>> a = [[1, 2, 3], [4, 5, 6]]
>>> b = 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
[[1, 10, 3], [4, 5, 6]]

list() 和 [:] 改变其他层,除了第一层

# =========== [:] ===========
>>> a = [[1, 2, [3.5, 6]], [4, 5, 6]]
>>> b = a[:]
>>> a
[[1, 2, [3.5, 6]], [4, 5, 6]]
>>> b
[[1, 2, [3.5, 6]], [4, 5, 6]]
>>> a[0][2] = 4
>>> a
[[1, 2, 4], [4, 5, 6]]
>>> b
[[1, 2, 4], [4, 5, 6]]


>>> a = [[1, 2, [3.5, 6]], [4, 5, 6]]
>>> b = a[:]
>>> a
[[1, 2, [3.5, 6]], [4, 5, 6]]
>>> b
[[1, 2, [3.5, 6]], [4, 5, 6]]
>>> a[0][2][0] = 999
>>> a
[[1, 2, [999, 6]], [4, 5, 6]]
>>> b
[[1, 2, [999, 6]], [4, 5, 6]]



# =========== list() ===========
>>> a = [[1, 2, [3.5, 6]], [4, 5, 6]]
>>> b = list(a)
>>> a
[[1, 2, [3.5, 6]], [4, 5, 6]]
>>> b
[[1, 2, [3.5, 6]], [4, 5, 6]]
>>> a[0][2] = 4
>>> a
[[1, 2, 4], [4, 5, 6]]
>>> b
[[1, 2, 4], [4, 5, 6]]


>>> a = [[1, 2, [3.5, 6]], [4, 5, 6]]
>>> b = list(a)
>>> a
[[1, 2, [3.5, 6]], [4, 5, 6]]
>>> b
[[1, 2, [3.5, 6]], [4, 5, 6]]
>>> a[0][2][0] = 999
>>> a
[[1, 2, [999, 6]], [4, 5, 6]]
>>> b
[[1, 2, [999, 6]], [4, 5, 6]]

经过copy()

你会发现这个功能和 andcopy()是一样的。它们都是浅拷贝list()[:]

关于浅拷贝和深拷贝的更多信息,也许你可以参考这里

>>> a = [[1, 2, 3], [4, 5, 6]]
>>> b = copy.copy(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
[[1, 10, 3], [4, 5, 6]]

经过deepcopy()

>>> import copy
>>> a = [[1, 2, 3], [4, 5, 6]]
>>> b = copy.deepcopy(a)
>>> a
[[1, 2, 3], [4, 5, 6]]
>>> b
[[1, 2, 3], [4, 5, 6]]
>>> a[0] = 1
>>> a
[1, [4, 5, 6]]
>>> b
[[1, 2, 3], [4, 5, 6]]


>>> a = [[1, 2, 3], [4, 5, 6]]
>>> b = copy.deepcopy(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
[[1, 2, 3], [4, 5, 6]]

于 2020-12-16T01:31:48.500 回答
1

以下是如何深度复制 2D 列表的示例:

  b = [x[:] for x in a]
于 2020-04-10T13:26:15.697 回答
0

如果您不允许直接导入模块,您可以将自己的 deepcopy 函数定义为 -

def copyList(L):
if type(L[0]) != list:
    return [i for i in L]
else:
    return [copyList(L[i]) for i in range(len(L))]

它的工作很容易被视为 -

>>> x = [[1,2,3],[3,4]]
>>> z = copyList(x)
>>> x
[[1, 2, 3], [3, 4]]
>>> z
[[1, 2, 3], [3, 4]]
>>> id(x)
2095053718720
>>> id(z)
2095053718528
>>> id(x[0])
2095058990144
>>> id(z[0])
2095058992192
>>>
于 2021-01-29T08:45:49.313 回答
-1

只是一个递归的深拷贝函数。

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

编辑:正如 Cfreak 提到的,这已经在copy模块中实现。

于 2013-07-26T06:33:38.073 回答
-1

将列表视为一棵树,python 中的 deep_copy 可以最简洁地写为

def deep_copy(x):
    if not isinstance(x, list):
        return x
    else:
        return [deep_copy(elem) for elem in x]

它基本上是以深度优先的方式递归遍历列表。

于 2013-12-13T18:08:30.360 回答
-3

这更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.

注意:这对于引用对象列表是不安全的

于 2016-11-29T14:55:02.217 回答