4

我一直在为这个问题撞墙。我创建了一个列表并制作了 4 个副本,其中只有一个共享相同的内存索引。如果我更改原始列表,也会以某种方式更改其中的 3 个副本,其中 2 个具有不同的内存索引。只有当我使用与原始命令相同的命令创建列表时,我才能创建不受原始更改影响的列表。这怎么可能?这是我的控制台的输出:

>>> orig=[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]]
>>> id(orig)
151498220
>>> copy1=orig   #same index
>>> id(copy1)
151498220
>>> copy2=orig[:]   #different index
>>> id(copy2)
151498348
>>> copy3=list(orig)   #different index
>>> id(copy3)
151503020
>>> copy4=[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]]
>>> id(copy4)
151498636
>>> orig[0][1]=34
>>> copy1
[[0, 34, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]]   #expected, same memory index
>>> copy2
[[0, 34, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]]   #WTF?!?!?
>>> copy3
[[0, 34, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]]   #ARGH!!!
>>> copy4
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]]   #workaround?
>>> id(orig)
151498220
>>> id(copy1)
151498220
>>> id(copy2)
151498348
>>> id(copy3)
151503020
>>> id(copy4)
151498636

内存索引没有改变,但列表被改变了。只有 copy1 应该更改,因为它具有与 orig 相同的内存索引。

4

3 回答 3

10

那是因为你只是在创建一个浅拷贝。您需要创建一个深层副本。

根据复制模块文档:

  • 浅拷贝构造一个新的复合对象,然后(在可能的范围内)将对原始对象中的对象的引用插入其中。
  • 深拷贝构造一个新的复合对象,然后递归地将在原始对象中找到的对象的副本插入其中。

您可以通过比较内部列表的 id 来验证它:

>>> orig=[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]]
>>> id(orig)
151498220
>>> copy2=orig[:]   #different index
>>> id(copy2)
151498348

>>> id(copy2[0]) == id(orig[0])  # inner list have same id
True

您可以创建一个deepcopy使用copy.deepcopy(x)

>>> import copy
>>> 
>>> copy3 = copy.deepcopy(orig)
>>> 
>>> id(copy3[0]) == id(orig[0])   # inner list have different id
False

>>> orig[0][3] = 34
>>> 
>>> orig
[[0, 34, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]]
>>> copy3
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]]
于 2013-07-17T18:46:27.783 回答
0

您的列表是名称列表,而不是您所想的列表列表。当您使用您列出的任何方法(切片、基于旧列表创建新列表等)制作列表副本时,您会创建一个新的外部列表,但新列表中的名称引用相同的内部列表旧的名字。

# One through three are all examples of:

first_list, second_list, third_list = [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 123, 0]
original = [first_list, second_list, third_list]
another_list = original[:]

# We do indeed have another list
assert id(original) != id(another_list)

# But the *references* in the list are pointing at the same underlying child list
assert id(original[0]) == id(another_list[0])
于 2013-07-17T18:46:05.487 回答
0

这是因为虽然外部列表(copy1copy2等)是单独的对象,但它们的子列表都是对您在 中创建的相同列表的引用orig。尝试做:

id(orig[0]) == id(copy3[0]) #should return True

为了制作列表对象及其引用的所有对象的深层副本,请使用deepcopy.

于 2013-07-17T18:47:46.607 回答