问题描述
我确定这已经在某处得到了回答,但我不知道如何描述它.
假设我想创建一个包含 3 个空列表的列表,如下所示:
lst = [[], [], []]
我以为我这样做很聪明:
lst = [[]] * 3
但我发现,在调试了一些奇怪的行为后,这会导致对一个子列表的追加更新,比如 lst[0].append(3)
,更新整个列表,使其 [[3], [3], [3]] 而不是[[3], [], []]
.
但是,如果我用
初始化列表lst = [[] for i in range(3)]
然后执行 lst[1].append(5)
给出预期的 [[], [5], []]
我的问题是为什么会发生这种情况?有趣的是,如果我这样做
lst = [[]]*3lst[0] = [5]lst[0].append(3)
然后单元格 0 的链接"被破坏,我得到 [[[5,3],[],[]]
,但是 lst[1].append(0)
仍然导致 [[5,3],[0],[0]
.
我最好的猜测是使用 [[]]*x
形式的乘法会导致 Python 存储对单个单元格的引用......?
是的.你可以自己测试一下
>>>lst = [[]] * 3>>>打印 [id(x) for x in lst][11124864、11124864、11124864]这表明所有三个引用都指向同一个对象.请注意,这种情况确实完全合情合理.它只是复制值,在这种情况下,值是引用.这就是为什么您会看到相同的引用重复了 3 次.
有趣的是,如果我这样做
lst = [[]]*3lst[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]
也看到这个变化.
这是人们在使用指针和引用时犯的一个典型错误.这是一个简单的类比.你有一张纸.在上面,你写下某人房子的地址.你现在拿起那张纸,复印两次,最后得到三张写有相同地址的纸.现在,拿第一张纸,写下写在上面的地址,然后写一个新地址到别人的家.另外两张纸上写的地址有没有变?不.不过,这正是您的代码所做的.这就是 其他两项不变的原因.此外,想象一下第二张纸上仍然地址的房子的所有者为他们的房子建造了一个附加车库.现在我问你,地址在第三张纸上的房子有附加车库吗?是的,确实如此,因为它与第二张纸上写着地址的房子完全是同一所房子.这解释了关于您的第二个代码示例的一切.
:您没想到 Python 会调用复制构造函数",是吗?咳咳.
I'm sure this has been answered somewhere but I wasn't sure how to describe it.
Let's say I want to create a list containing 3 empty lists, like so:
lst = [[], [], []]
I thought I was being all clever by doing this:
lst = [[]] * 3
But I discovered, after debugging some weird behavior, that this caused an append update to one sublist, say lst[0].append(3)
, to update the entire list, making it [[3], [3], [3]]
rather than [[3], [], []]
.
However, if I initialize the list with
lst = [[] for i in range(3)]
then doing lst[1].append(5)
gives the expected [[], [5], []]
My question is why does this happen? It is interesting to note that if I do
lst = [[]]*3
lst[0] = [5]
lst[0].append(3)
then the 'linkage' of cell 0 is broken and I get [[5,3],[],[]]
, but lst[1].append(0)
still causes [[5,3],[0],[0]
.
My best guess is that using multiplication in the form [[]]*x
causes Python to store a reference to a single cell...?
Yes. And you can test this yourself
>>> lst = [[]] * 3
>>> print [id(x) for x in lst]
[11124864, 11124864, 11124864]
This shows that all three references refer to the same object. And note that it really makes perfect sense that this happens. It just copies the values, and in this case, the values are references. And that's why you see the same reference repeated three times.
lst = [[]]*3
lst[0] = [5]
lst[0].append(3)
You changed the reference that occupies lst[0]
; that is, you assigned a new value to lst[0]
. But you didn't change the value of the other elements, they still refer to the same object that they referred to. And lst[1]
and lst[2]
still refer to exactly the same instance, so of course appending an item to lst[1]
causes lst[2]
to also see that change.
This is a classic mistake people make with pointers and references. Here's the simple analogy. You have a piece of paper. On it, you write the address of someone's house. You now take that piece of paper, and photocopy it twice so you end up with three pieces of paper with the same address written on them. Now, take the first piece of paper, scribble out the address written on it, and write a new address to someone else's house. Did the address written on the other two pieces of paper change? No. That's exactly what your code did, though. That's why the other two items don't change. Further, imagine that the owner of the house with address that is still on the second piece of paper builds an add-on garage to their house. Now I ask you, does the house whose address is on the third piece of paper have an add-on garage? Yes, it does, because it's exactly the same house as the one whose address is written on the second piece of paper. This explains everything about your second code example.
: You didn't expect Python to invoke a "copy constructor" did you? Puke.
这篇关于使用乘法 ( * ) 意外行为生成子列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!