我确信这个问题已经在某个地方得到了回答,但我不确定如何描述它。
假设我想创建一个包含 3 个空列表的列表,如下所示:
lst = [[], [], []]
我以为我这样做很聪明:
lst = [[]] * 3
但我发现,在调试一些奇怪的行为后,这会导致对一个子列表进行追加更新,例如
lst[0].append(3)
,以更新整个列表,使其成为 [[3], [3], [3]]
而不是 [[3], [], []]
。
但是,如果我用
初始化列表lst = [[] for i in range(3)]
然后做
lst[1].append(5)
给出预期的 [[], [5], []]
我的问题是为什么会发生这种情况?有趣的是,如果我这样做
lst = [[]]*3
lst[0] = [5]
lst[0].append(3)
然后单元格 0 的“链接”被破坏,我得到
[[5,3],[],[]]
,但是 lst[1].append(0)
仍然导致 [[5,3],[0],[0]
。
我最好的猜测是,使用
[[]]*x
形式的乘法会导致Python存储对单个单元格的引用......?
我最好的猜测是,使用
形式的乘法会导致Python存储对单个单元格的引用......?[[]] * x
是的。你可以自己测试一下
>>> lst = [[]] * 3
>>> print [id(x) for x in lst]
[11124864, 11124864, 11124864]
这表明所有三个引用都引用同一个对象。请注意,发生这种情况真的完全有道理1。它只是复制值,在本例中,值是引用。这就是为什么您会看到相同的引用重复三次。
有趣的是如果我这样做
lst = [[]]*3
lst[0] = [5]
lst[0].append(3)
然后单元格 0 的“链接”被破坏,我得到
,但是[[5,3],[],[]]
仍然导致lst[1].append(0)
。[[5,3],[0],[0]
您更改了占据
lst[0]
的引用;也就是说,您为 lst[0]
分配了一个新的 value。但是您没有更改其他元素的value,它们仍然引用它们引用的同一个对象。并且
lst[1]
和 lst[2]
仍然引用完全相同的实例,因此当然将一个项目附加到 lst[1]
会导致 lst[2]
也看到该更改。
这是人们在使用指针和引用时犯的一个典型错误。这是一个简单的类比。你有一张纸。在上面,你写下某人房子的地址。现在,您拿起那张纸,复印两次,最终得到三张纸,上面写着相同的地址。现在,拿出第一张纸,写下上面写的地址,然后写一个到别人房子的新地址。另外两张纸上写的地址有没有变?不。不过,这正是您的代码所做的。这就是为什么其他两项没有改变。此外,想象一下第二张纸上地址为“still”的房子的主人在他们的房子里建造了一个附加车库。现在我问你,第第三纸上地址的房子有附加车库吗?是的,确实如此,因为它与地址写在“第二张”纸上的房子“一模一样”。这解释了关于第二个代码示例的一切。 1:你没想到 Python 会调用“复制构造函数”,是吗?呕吐。 这是因为序列乘法只是重复引用。当您编写
[[]] * 2
时,您创建了一个包含两个元素的新列表,但这两个元素都是内存中的 same 对象,即空列表。因此,一个方面的变化会反映在另一个方面。相比之下,推导式在每次迭代时都会创建一个
新的、独立的>>> l1 = [[]] * 2
>>> l2 = [[] for _ in xrange(2)]
>>> l1[0] is l1[1]
True
>>> l2[0] is l2[1]
False
他们引用相同的列表。 还有类似的问题这里
和这里
>>> a = []
>>> b = [a]
>>> c = b * 3 # c now contains three references to a
>>> d = [ a for _ in xrange(4) ] # and d contains four references to a
>>> print c
[[], [], []]
>>> print d
[[], [], [], []]
>>> a.append(3)
>>> print c
[[3], [3], [3]]
>>> print d
[[3], [3], [3], [3]]
>>> x = [[]] * 3 # shorthand equivalent to c
>>> print x
[[], [], []]
>>> x[0].append(3)
>>> print x
[[3], [3], [3]]
上面相当于你的第一个例子。现在每个列表都有自己的变量,希望更清楚原因。
c[0] is c[1]
True
,因为两个表达式计算结果为同一个对象 (
a
)。
您的第二个示例创建了多个不同的内部列表对象。
>>> c = [[], [], []] # this line creates four different lists
>>> d = [ [] for _ in xrange(3) ] # so does this line
>>> c[0].append(4)
>>> d[0].append(5)
>>> print c
[[4], [], []]
>>> print d
[[5], [], []]