使用乘法(*)意外行为生成子列表 [英] Generating sublists using multiplication ( * ) unexpected behavior

查看:67
本文介绍了使用乘法(*)意外行为生成子列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我确定已经在某个地方回答了这个问题,但是我不确定如何描述它.

I'm sure this has been answered somewhere but I wasn't sure how to describe it.

假设我要创建一个包含3个空列表的列表,如下所示:

Let's say I want to create a list containing 3 empty lists, like so:

lst = [[], [], []]

我以为我很聪明:

lst = [[]] * 3

但是我发现,在调试了一些奇怪的行为之后,这导致对一个子列表(例如lst[0].append(3))进行追加更新,从而更新了整个列表,使其成为[[3], [3], [3]]而不是[[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)]

然后做lst[1].append(5)会得到预期的[[], [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)

然后单元格0的链接"断开,我得到[[5,3],[],[]],但是lst[1].append(0)仍然会导致[[5,3],[0],[0].

then the 'linkage' of cell 0 is broken and I get [[5,3],[],[]], but lst[1].append(0) still causes [[5,3],[0],[0].

我最好的猜测是,使用[[]]*x形式的乘法会导致Python存储对单个单元格的引用...?

My best guess is that using multiplication in the form [[]]*x causes Python to store a reference to a single cell...?

推荐答案

我最好的猜测是,使用[[]] * x形式的乘法会导致Python将对单个单元格的引用存储起来...?

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]

这表明所有三个引用都引用同一对象.并请注意,真的完全可以理解为发生这种情况 1 .它只是复制,在这种情况下,值引用.这就是为什么您看到同一参考重复了三遍的原因.

This shows that all three references refer to the same object. And note that it really makes perfect sense that this happens1. 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.

有趣的是,如果我这样做

It is interesting to note that if I do

lst = [[]]*3
lst[0] = [5]
lst[0].append(3)

然后单元格0的链接"断开,我得到[[5,3],[],[]],但是lst[1].append(0)仍然会导致[[5,3],[0],[0].

then the 'linkage' of cell 0 is broken and I get [[5,3],[],[]], but lst[1].append(0) still causes [[5,3],[0],[0].

您更改了占用lst[0]的引用;也就是说,您为lst[0]分配了新的.但是您没有更改其他元素的 value ,它们仍然引用与它们所引用的对象相同的对象.并且lst[1]lst[2]仍然引用完全相同的实例,因此,在lst[1]后面附加一个项目当然会导致lst[2]也看到该更改.

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.

这是人们使用指针和引用犯下的经典错误.这是简单的类比.你有一张纸.在上面写上某人房屋的地址.现在,您拿起那张纸,并将其影印两次,以便最终获得三张纸,并在上面写上相同的地址.现在,拿出第一张纸,涂写在上面的地址,然后将新地址写到别人的家.写在其他两张纸上的地址是否发生变化?不.那完全是您的代码所做的.这就是为什么为什么其他两项没有更改.进一步,假设第二张纸上地址为 still 的房子的所有者为他们的房子建造了一个附加车库.现在我问你,地址在第三张纸上的房子是否有一个附加车库?是的,确实如此,因为它与地址写在 second 纸上的房子完全相同.这解释了关于第二个代码示例的所有内容.

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.

1 :您没想到Python会调用复制构造函数"吗?呕吐.

1: You didn't expect Python to invoke a "copy constructor" did you? Puke.

这篇关于使用乘法(*)意外行为生成子列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆