无法理解浅拷贝

时间:2013-08-16 02:38:20

标签: python deep-copy shallow-copy

>>> a = [1,2,3]
>>> b = a[:]
>>> id(a[0]) == id(b[0])
True
>>> b[0] = 99
>>> id(a[0]) == id(b[0])
False

据我所知,要制作浅色副本,我们可以使用切片,还有一个复制模块。但是为什么写入“b”索引会改变id。

4 个答案:

答案 0 :(得分:5)

第1部分:为什么调用b[0] = 99导致a[0] == b[0] >> False

你的问题的答案是,当你执行b[0] = 99时,你没有“修改B字段中的一个指向的内存地址”,你实际上改变了其中一个B是田野点。

a = [1,2,3]  

现在a包含对list对象的引用,该对象又包含对三个int对象的三个引用。

b = a[:]

现在b引用list对象(与a引用的对象不同),并包含对三个int对象的三个引用,{{1} }指的。

a

错误,因为id(a) == id(b) #False a是对不同b个对象的引用。

list

是的,因为id(a[0]) == id(b[0]) #True a[0]对象的引用,1也是如此。只有一个b[0]对象,1a[0]

引用它
b[0]

b[0] = 99 # ^^^^^^ THIS IS NOT WHAT THE WIKIPEDIA ARTICLE IS DESCRIBING 仍然引用相同的旧b对象,但list现在引用b[0]对象。您只需将99引用的list中的一个引用替换为对不同对象的新引用。您已经修改了b指向的任何对象!

a

错误,因为id(a[0]) == id(b[0]) #False 对象与1对象不是同一个对象。


第2部分:维基百科文章在谈论什么呢?

这是一个复制操作的示例,其中您实际上是“修改B字段之一指向的内存地址”,因此您可以看到复制对象中的后续更改。

99

a = [[1],[2],[3]] 是对a对象的引用,但现在list对象包含对list个对象的三个引用,每个对象都包含对{{1}的引用对象。

list

和以前一样,您已经int引用了一个新的,不同的b = a[:] 对象,该对象引用了b中引用的三个list个对象。 。这是证据:

list

错误,因为之前aid(a) == id(b) # False 是对不同a个对象的引用。

b

是的,因为和以前一样,listid(a[0]) == id(b[0]) #True 都指的是同一个对象。这次它是一个a[0]对象,它不是不可变的,与b[0]对象不同 - 我们实际上可以更改list对象的内容! 以下是不同之处:

int

我们做到了 - 我们更改了list

引用的b[0][0] = 99 对象的内容
list

请参阅? b[0]引用的a[0][0] # 99 !!!!!!!!!!!!!! Wikipedia doesn't need to be edited! 引用的“列表”现在引用list对象,而不是a对象,因为它是相同的{{1}您使用99访问的内容。简单,呵呵;)

1

是的,因为虽然我们更改了引用list的内容,但我们 更改了b[0][0] = 99id(a[0]) == id(b[0]) #True !!! 中的引用本身 - 这是更接近的维基百科文章在“浅拷贝”部分中描述的内容。

答案 1 :(得分:1)

  

但为什么写入“b”索引会改变id。

因为他们现在是不同的事情。如果您要检查aa[0]仍然是1而不是99,因为b是副本。如果您不想要这种行为,则不会进行复制:

>>> a = [1,2,3]
>>> b = a
>>> b[0] = 99
>>> a[0]
99

相反,你有这个:

>>> a = [1,2,3]
>>> b = a[:]
>>> b[0] = 99
>>> a[0]
1

由于您标记了deepcopy ...只有在您的列表本身包含可变参数时才会有用。比方说,你有:

>>> from copy import deepcopy
>>> a = [[1,2],[3,4]]
>>> b = a
>>> c = a[:]
>>> d = deepcopy(a)

所以abc是浅版,d是深层副本。

>>> b[0] = 3
>>> a
[3, [3,4]]
>>> c
[[1,2], [3,4]]
>>> d
[[1,2], [3,4]]

ba相同,但副本不受影响。

>>> c[1][1] = 'hi'
>>> a
[3, [3, 'hi']]
>>> c
[[1, 2], [3, 'hi']]

如果您替换c的条目,则a不受影响。但是如果你仍然在条目中有原始列表,那么修改一个列表仍会显示在另一个列表中。嵌套列表仍然相同。

>>> d[1][1] = 10
>>> a
[3, [3, 'hi']]
>>> d
[[1, 2], [3, 10]]

由于d是一个深层副本,它复制了列表及其嵌套列表,因此我们可以随意修改及其元素,而无需担心其他副本的混乱

答案 2 :(得分:1)

当您将a的浅层副本复制到bb = a[:])时,您将其复制了。 b在特定时间点成为a的副本 - 它是a的“快照”。

更新b[0] = 99后,您更新了b - a的副本。你没有更新a。这是制作浅(或深)副本的重点 - 您想要一个具有相同内容的新变量,这样您就可以对副本进行更改而不会影响原始副本。

如果您希望b[0] = 99也影响a,那么您不想“复制”a,您只想通过其他名称引用它。你使用b = a

答案 3 :(得分:1)

Python中的列表包含对其内容的引用。

使用a[:]copy方法复制列表时,可以使用相同的引用创建新列表。更改原始列表中的项目时会发生什么情况取决于其类型。

可变对象(如列表)可以就地更改(这就是使它们可变的原因)。两个列表仍然引用相同的对象:

a = [[0],[1],[2]]
b = a[:]
a[0].append[1]
b[0] # >>> [0,1]

另一方面,整数是不可变的。更改整数会创建一个新对象,并带有新的id和引用。

a = [0, 1, 2]
b = a[:]
a[0] = 10
b[0] # >>> 0