如何深拷贝一个列表?
Posted
技术标签:
【中文标题】如何深拷贝一个列表?【英文标题】:How to deep copy a list? 【发布时间】:2013-07-26 06:04:26 【问题描述】:在E0_copy = list(E0)
之后,我猜E0_copy
是E0
的深层副本,因为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]]
【问题讨论】:
另外,b = a[:] 是浅拷贝。参考***.com/questions/16270374/… 【参考方案1】:E0_copy
不是深拷贝。您不会使用 list()
进行深层复制。 (list(...)
和 testList[:]
都是浅拷贝。)
您使用copy.deepcopy(...)
深度复制列表。
deepcopy(x, memo=None, _nil=[])
Deep copy operation on arbitrary Python objects.
看下面的sn-p-
>>> 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)
,您可以看到浅复制引用了内部列表。
【讨论】:
list1.append(list2) 也是list2的浅拷贝 请记住,copy.deepcopy
是 incredibly slow 相对于列表切片(大约 20 倍)。 Implementing __deepcopy__
在课堂上可以帮助加快速度。
@Lazik 嗯?不,这不对。你从哪里得到那个的?也许你的意思是list1 = []; list1.extend(list2)
?【参考方案2】:
如果您的列表元素是不可变对象,那么您可以使用它,否则您必须使用来自copy
模块的deepcopy
。
你也可以像这样使用最短的方法来深拷贝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]
【讨论】:
这不是深拷贝。 那是什么。它有两个具有相同值的不同字典(您可以检查每个字典的 id)。 Read this, [:] 只是创建一个浅拷贝,它不会递归地创建其中对象的拷贝。 谢谢。你的意思是说如果我们使用这个,新列表将被创建,但新列表的所有元素将只是副本,它们将具有与前一个相同的对象(相同的 id)? 尝试使用嵌套列表。更新列表 a 的嵌套项。它也会在列表 b 中更新。这意味着 a[:] 不是深拷贝。【参考方案3】:我相信很多程序员都遇到过面试问题,他们被要求深度复制一个链表,但是这个问题比听起来更难!
在 Python 中,有一个名为 copy
的模块具有两个有用的功能:
import copy
copy.copy()
copy.deepcopy()
copy()
是一个浅拷贝函数。如果给定的参数是一个复合数据结构,例如 list,那么 Python 将创建另一个相同类型的对象(在这种情况下,一个 new list)但是旧列表中的所有内容,仅复制它们的引用。可以这样想:
newList = [elem for elem in oldlist]
直观地说,我们可以假设deepcopy()
将遵循相同的范例,唯一的区别是对于每个 elem 我们将递归调用 deepcopy,(就像mbguy's answer)
但这是错误的!
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,从而防止不必要的重复,从而保留复制的结构复合数据。
Official docs
【讨论】:
应更新 deepcopy 文档,以突出包含此答案末尾的警告/示例。【参考方案4】:将列表视为一棵树,python中的deep_copy可以最简洁的写成
def deep_copy(x):
if not isinstance(x, list):
return x
else:
return [deep_copy(elem) for elem in x]
它基本上是以深度优先的方式递归遍历列表。
【讨论】:
【参考方案5】:@Sukrit 卡拉
No.1:list()
、[:]
、copy.copy()
都是浅拷贝。如果一个对象是复合的,它们都不合适。您需要使用copy.deepcopy()
。
No.2:直接b = a
,a
和b
具有相同的引用,更改a
与更改b
一样。
将 a 设置为 b
如果直接将a
分配给b
,a
和b
共享一个引用。
>>> 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()
你会发现copy()
的功能和list()
和[:]
是一样的。都是浅拷贝。
关于浅拷贝和深拷贝的更多信息,也许你可以参考here。
>>> 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]]
【讨论】:
请edit 回答您的问题,包括解释它是如何工作的以及为什么它可以解决问题中描述的问题。见How to Answer。 Here is the much more detailed case【参考方案6】:如果不允许直接导入模块,可以将自己的 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
>>>
【讨论】:
【参考方案7】:下面是一个如何深度复制 2D 列表的示例:
b = [x[:] for x in a]
【讨论】:
你测试了这个例子吗?a = [3, 4, 5] b = [x[:] for x in a] Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 1, in <listcomp> TypeError: 'int' object is not subscriptable
啊,我是为二维列表写的。 a = [[0,1],[2,3]]。我已经编辑了答案以突出显示这一点。【参考方案8】:
这更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.
注意:这对于引用对象列表是不安全的
【讨论】:
这不起作用。我认为它可能只是刚刚检查过。尝试以字典列表为例 @ShashankSingh 是的,这不适用于字典列表,因为条目是引用标签(指向内存位置)。因此,使用此方法复制字典列表将创建一个新列表,但由于条目是字典,它们仍将引用相同的内存位置。【参考方案9】:如果列表的内容是原始数据类型,则可以使用推导式
new_list = [i for i in old_list]
您可以将它嵌套在多维列表中,例如:
new_grid = [[i for i in row] for row in grid]
【讨论】:
【参考方案10】:只是一个递归的深拷贝函数。
def deepcopy(A):
rt = []
for elem in A:
if isinstance(elem,list):
rt.append(deepcopy(elem))
else:
rt.append(elem)
return rt
编辑:正如 Cfreak 提到的,这已经在 copy
模块中实现。
【讨论】:
没有理由在copy
模块中重新实现标准的deepcopy()
函数以上是关于如何深拷贝一个列表?的主要内容,如果未能解决你的问题,请参考以下文章