在这种特殊情况下,由于列表的元素是小整数,Python 有一个内置机制来引用“相同”对象(整数),而不允许对深度复制的列表进行更改以导致更改从中复制它的列表。
这是一个像你这样的整数的例子:
In [135]: import copy
In [136]: a1 = [1, 2, 3]
In [137]: a2 = copy.copy(a1)
In [138]: a3 = copy.deepcopy(a1)
In [139]: map(id, a1)
Out[139]: [26960216, 26960192, 26960168]
In [140]: map(id, a2)
Out[140]: [26960216, 26960192, 26960168]
In [141]: map(id, a3)
Out[141]: [26960216, 26960192, 26960168]
所以在这一点上我们可以看到列表包含整数,都具有相同的 id。让我们更改深层副本中的一个元素。
In [142]: a3[0] = 1000
In [143]: map(id, a1)
Out[143]: [26960216, 26960192, 26960168]
In [144]: map(id, a2)
Out[144]: [26960216, 26960192, 26960168]
In [145]: map(id, a3)
Out[145]: [39759800, 26960192, 26960168]
现在a3 的第一个条目有一个新的 id,同时其他列表保持不变。现在让我们更改浅拷贝的第一个条目。
In [146]: a2[0] = 1000
In [147]: map(id, a1)
Out[147]: [26960216, 26960192, 26960168]
In [148]: map(id, a2)
Out[148]: [39759200, 26960192, 26960168]
In [149]: map(id, a3)
Out[149]: [39759800, 26960192, 26960168]
注意整数 1000,它作为a2 和a3 的第一个条目,有一个不同的 id 值。
这样做的原因是Python运行时实际上缓存了一些小整数和其他不可变对象,这意味着它们被引用的任何地方,都是对单个缓存值的引用。
这里是a source describing it:
当前的实现为 -5 到 256 之间的所有整数保留一个整数对象数组,当您在该范围内创建一个 int 时,实际上您只是获取对现有对象的引用。所以应该可以改变 1 的值。我怀疑 Python 在这种情况下的行为是未定义的。 :-)
要查看deepcopy 和copy 具有显着不同行为的示例,我们需要deepcopy 对copy 的递归 调用将产生影响——而这缓存的小整数不会发生。
让我们尝试使用列表列表,我们将修改内容,而不是突然改变最顶层的列表元素所指的内容:
In [171]: a1 = [[1], [2], [3]]
In [172]: a2 = copy.copy(a1); a3 = copy.deepcopy(a1)
In [173]: a1
Out[173]: [[1], [2], [3]]
In [174]: a2
Out[174]: [[1], [2], [3]]
In [175]: map(id, a1)
Out[175]: [140608561277264, 140608561418040, 140608561277120]
In [176]: map(id, a2)
Out[176]: [140608561277264, 140608561418040, 140608561277120]
In [177]: a2[0][0] = 1000
In [178]: a1
Out[178]: [[1000], [2], [3]]
In [179]: a2
Out[179]: [[1000], [2], [3]]
In [180]: a3
Out[180]: [[1], [2], [3]]
In [181]: a3[1][0] = 1001
In [182]: a1
Out[182]: [[1000], [2], [3]]
In [183]: a2
Out[183]: [[1000], [2], [3]]
In [184]: a3
Out[184]: [[1], [1001], [3]]