使用乘法(*)产生意外行为的子列表

发布于 2021-01-29 16:16:08

这个问题已经在这里有了答案

列表更改列表意外地反映在子列表中 (16个答案)

嵌套列表索引[重复项] (2个答案)

7年前关闭。

我确定这已经在某处得到了回答,但是我不确定如何描述它。

假设我要创建一个包含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存储对单个单元格的引用…?

关注者
0
被浏览
47
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

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

    是。你可以自己测试

    >>> 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]。但是您没有更改其他元素的
    ,它们仍然引用与它们所引用的对象相同的对象。并且lst[1]并且lst[2]仍然指的是完全相同的实例,因此,当然将项目附加到也会lst[1]导致lst[2]该更改。

    这是人们使用指针和引用犯的一个经典错误。这是简单的类比。你有一张纸。在上面写上某人房屋的地址。现在,您拿起那张纸,并将其影印两次,以便最终得到三张纸,上面写有相同的地址。现在,拿起
    第一 张纸,写下上面写的地址,然后再写一个新地址到 别人的 房子里。另外两张纸上写的地址是否发生变化?不。那 正是 您的代码所做的。这就是
    为什么 其他两项不变的原因。进一步,假设地址 仍然 是房屋的所有者 __在第二张纸上,为他们的房子建了一个附加车库。 现在我问你,地址在 第三
    张纸上的房子是否有一个附加车库?是的,是的,因为它 地址写在 第二 张纸上的房子 完全 一样。这说明了有关第二个代码示例的 所有 内容。

    1:您没想到Python会调用“复制构造函数”吗?uke



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看