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

4 回答 4

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对象的三个引用,与引用的对象相同a

id(a) == id(b)
#False

错误,因为ab是对不同list对象的引用。

id(a[0]) == id(b[0])
#True

是的,因为a[0]是对对象的引用1,所以也是b[0]。只有一个1对象,它由a[0]and引用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

False,因为1对象与对象不是同一个99对象。


第 2 部分:那么,那篇维基百科文章到底在说什么?

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

a = [[1],[2],[3]]

a是对list对象的引用,但现在该list对象包含三个对list对象的引用,每个引用都包含对一个int对象的引用。

b = a[:]

和以前一样,您已经b引用了一个新的、不同的list对象,它引用了 .samelist中提到的三个对象a。这是证据:

id(a) == id(b)
# False

错误,因为和以前一样ab都是对不同list对象的引用。

id(a[0]) == id(b[0]) 
#True

没错,因为和以前一样,两者a[0]b[0]指的是同一个对象。这次它是一个list对象,它不是不可变的,不像一个int对象——我们实际上可以改变list对象的内容! 这是它变得不同的地方:

b[0][0] = 99  

我们做到了——我们改变了引用的对象的内容listb[0]

a[0][0]
# 99 !!!!!!!!!!!!!!  Wikipedia doesn't need to be edited!

看?list现在被引用的“列表”a指的是99对象,而不是1对象,因为它与list您使用 访问的相同b[0][0] = 99。很简单,呵呵;)

id(a[0]) == id(b[0])
#True !!!

是的,因为虽然我们更改了a[0]引用的内容,但我们并没有更改引用本身,a[0]并且b[0]- 这更接近 Wikipedia 文章在“浅拷贝”部分中描述的内容。

于 2013-08-16T03:48:22.117 回答
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]]

b与 相同a,但副本不受影响。

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

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

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

由于d是深层副本,它复制了列表及其嵌套列表,因此我们可以随意修改它及其元素,而不必担心会弄乱其他副本。

于 2013-08-16T02:48:39.580 回答
1

当您制作ato b( b = a[:]) 的浅拷贝时,您复制了它。 在特定时间点b成为副本——它是 a 的“快照”。a

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

如果你也想b[0] = 99影响a,那么你不想“复制” a,你只想用另一个名字来引用它。你会用b = a.

于 2013-08-16T03:42:22.567 回答
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
于 2013-08-16T03:52:20.520 回答