Python 无法理解浅拷贝

Python 无法理解浅拷贝,python,deep-copy,shallow-copy,Python,Deep Copy,Shallow Copy,我知道,为了制作浅拷贝,我们可以使用切片,而且还有一个拷贝模块。但是为什么写入索引“b”会改变id呢 但是为什么写入索引“b”会改变id呢 因为他们现在不同了。如果要检查a,a[0]仍然是1而不是99,因为b是副本。如果您不希望出现这种行为,则不会复制: >>> a = [1,2,3] >>> b = a[:] >>> id(a[0]) == id(b[0]) True >>> b[0] = 99 >>>

我知道,为了制作浅拷贝,我们可以使用切片,而且还有一个拷贝模块。但是为什么写入索引“b”会改变id呢

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

因为他们现在不同了。如果要检查
a
a[0]
仍然是
1
而不是
99
,因为
b
是副本。如果您不希望出现这种行为,则不会复制:

>>> a = [1,2,3]
>>> b = a[:]
>>> id(a[0]) == id(b[0])
True
>>> b[0] = 99
>>> id(a[0]) == id(b[0])
False
相反,您有以下内容:

>>> a = [1,2,3]
>>> b = a
>>> b[0] = 99
>>> a[0]
99
因为您标记了
deepcopy
…只有当列表本身包含可变参数时才有意义。比如说,你有:

>>> a = [1,2,3]
>>> b = a[:]
>>> b[0] = 99
>>> a[0]
1
因此
a
b
c
是浅拷贝,
d
是深拷贝

>>> from copy import deepcopy
>>> a = [[1,2],[3,4]]
>>> b = a
>>> c = a[:]
>>> d = deepcopy(a)
b
a
相同,但副本不受影响

>>> b[0] = 3
>>> a
[3, [3,4]]
>>> c
[[1,2], [3,4]]
>>> d
[[1,2], [3,4]]
如果替换
c
的条目,
a
不受影响。但是,如果条目中仍有原始列表,则修改其中一个条目仍会显示在另一个条目中。嵌套列表仍然相同

>>> c[1][1] = 'hi'
>>> a
[3, [3, 'hi']]
>>> c
[[1, 2], [3, 'hi']]
由于
d
是一个深度副本,它复制了列表及其嵌套列表,因此我们可以随意修改它及其元素,而不用担心弄乱其他副本

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

因为他们现在不同了。如果要检查
a
a[0]
仍然是
1
而不是
99
,因为
b
是副本。如果您不希望出现这种行为,则不会复制:

>>> a = [1,2,3]
>>> b = a[:]
>>> id(a[0]) == id(b[0])
True
>>> b[0] = 99
>>> id(a[0]) == id(b[0])
False
相反,您有以下内容:

>>> a = [1,2,3]
>>> b = a
>>> b[0] = 99
>>> a[0]
99
因为您标记了
deepcopy
…只有当列表本身包含可变参数时才有意义。比如说,你有:

>>> a = [1,2,3]
>>> b = a[:]
>>> b[0] = 99
>>> a[0]
1
因此
a
b
c
是浅拷贝,
d
是深拷贝

>>> from copy import deepcopy
>>> a = [[1,2],[3,4]]
>>> b = a
>>> c = a[:]
>>> d = deepcopy(a)
b
a
相同,但副本不受影响

>>> b[0] = 3
>>> a
[3, [3,4]]
>>> c
[[1,2], [3,4]]
>>> d
[[1,2], [3,4]]
如果替换
c
的条目,
a
不受影响。但是,如果条目中仍有原始列表,则修改其中一个条目仍会显示在另一个条目中。嵌套列表仍然相同

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

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

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

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


如果您希望
b[0]=99
也影响
a
,那么您不想“复制”
a
,您只想用另一个名称引用它。当您将
a
的浅拷贝复制到
b
b=a[:]
)时,您会使用
b=a

b
在某个特定时间点成为a的副本,这是a的“快照”

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

如果您希望
b[0]=99
也影响
a
,那么您不想“复制”
a
,您只想用另一个名称引用它。您将使用
b=a

第1部分:调用
b[0]=99
为什么会导致
a[0]==b[0]>>False
您的问题的答案是,当您执行
b[0]=99
时,您不会“修改b的一个字段指向的内存地址”,而是实际更改b的一个字段指向的位置

>>> d[1][1] = 10
>>> a
[3, [3, 'hi']]
>>> d
[[1, 2], [3, 10]]
现在
a
包含对
列表
对象的引用,而该列表又包含对三个
int
对象的三个引用

a = [1,2,3]  
id(a) == id(b)
#False
id(a) == id(b)
# False
现在,b引用一个
列表
对象(与
a
引用的对象不同),并包含三个对三个
int
对象的引用,与
a
引用的对象相同

b = a[:]
错误,因为
a
b
是对不同
列表
对象的引用

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

id(a[0]) == id(b[0])
#True
b[0][0] = 99  
b
仍然指的是相同的
列表
对象,但
b[0]
现在指的是
99
对象。您只需将
b
引用的
列表中的一个引用替换为对不同对象的新引用。您没有修改
a
指向的任何对象

b[0] = 99  
# ^^^^^^ THIS IS NOT WHAT THE WIKIPEDIA ARTICLE IS DESCRIBING
错误,因为
1
对象与
99
对象不是同一个对象

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

第二部分:那维基百科的文章到底在说什么呢? 下面是一个复制操作的示例,您实际上正在“修改B的一个字段指向的内存地址”,以便可以看到复制对象中的后续更改

id(a[0]) == id(b[0]) 
#False
a
是对
list
对象的引用,但是现在
list
对象包含对
list
对象的三个引用,每个引用都包含对
int
对象的引用

a = [[1],[2],[3]]
与前面一样,您已使
b
引用一个新的、不同的
列表
对象,该对象引用