>>> 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。
>>> 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。
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
错误,因为a
和b
是对不同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
对象。
这是一个复制操作的示例,您实际上是在“修改 B 的一个字段指向的内存地址”,因此您可以看到复制对象的后续更改。
a = [[1],[2],[3]]
a
是对list
对象的引用,但现在该list
对象包含三个对list
对象的引用,每个引用都包含对一个int
对象的引用。
b = a[:]
和以前一样,您已经b
引用了一个新的、不同的list
对象,它引用了 .samelist
中提到的三个对象a
。这是证据:
id(a) == id(b)
# False
错误,因为和以前一样a
,b
都是对不同list
对象的引用。
id(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
指的是99
对象,而不是1
对象,因为它与list
您使用 访问的相同b[0][0] = 99
。很简单,呵呵;)
id(a[0]) == id(b[0])
#True !!!
是的,因为虽然我们更改了a[0]
引用的内容,但我们并没有更改引用本身,a[0]
并且b[0]
- 这更接近 Wikipedia 文章在“浅拷贝”部分中描述的内容。
但是为什么写入“b”的索引会改变id。
因为他们现在是不同的东西。如果要检查a
,a[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)
所以a
是b
,c
是浅拷贝,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']]
如果您替换 的条目c
,a
则不受影响。但是,如果条目中仍有原始列表,则修改一个仍会显示在另一个中。嵌套列表仍然相同。
>>> d[1][1] = 10
>>> a
[3, [3, 'hi']]
>>> d
[[1, 2], [3, 10]]
由于d
是深层副本,它复制了列表及其嵌套列表,因此我们可以随意修改它及其元素,而不必担心会弄乱其他副本。
当您制作a
to b
( b = a[:]
) 的浅拷贝时,您复制了它。 在特定时间点b
成为副本——它是 a 的“快照”。a
当您更新时b[0] = 99
,您更新b
了 - 的副本a
。你没有更新a
。这就是制作浅(或深)副本的重点——您想要一个具有相同内容的新变量,这样您就可以在不影响原始副本的情况下对副本进行更改。
如果你也想b[0] = 99
影响a
,那么你不想“复制” a
,你只想用另一个名字来引用它。你会用b = a
.
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