列表更改列表意外地反映在子列表中 [英] List of lists changes reflected across sublists unexpectedly

查看:80
本文介绍了列表更改列表意外地反映在子列表中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要在Python中创建一个列表列表,因此我输入了以下内容:

I needed to create a list of lists in Python, so I typed the following:

myList = [[1] * 4] * 3

列表看起来像这样:

[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]  

然后我更改了最里面的值之一:

Then I changed one of the innermost values:

myList[0][0] = 5

现在我的列表如下:

[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]  

这不是我想要或期望的.有人可以解释发生了什么,以及如何解决吗?

which is not what I wanted or expected. Can someone please explain what's going on, and how to get around it?

推荐答案

在编写[x]*3时,您基本上会得到列表[x, x, x].即,具有3个引用相同x的列表.然后,当您修改此单个x时,通过对它的所有三个引用都可以看到它:

When you write [x]*3 you get, essentially, the list [x, x, x]. That is, a list with 3 references to the same x. When you then modify this single x it is visible via all three references to it:

x = [1] * 4
l = [x] * 3
print(f"id(x): {id(x)}")
# id(x): 140560897920048
print(
    f"id(l[0]): {id(l[0])}\n"
    f"id(l[1]): {id(l[1])}\n"
    f"id(l[2]): {id(l[2])}"
)
# id(l[0]): 140560897920048
# id(l[1]): 140560897920048
# id(l[2]): 140560897920048

x[0] = 42
print(f"x: {x}")
# x: [42, 1, 1, 1]
print(f"l: {l}")
# l: [[42, 1, 1, 1], [42, 1, 1, 1], [42, 1, 1, 1]]

要修复此问题,您需要确保在每个位置创建一个新列表.一种方法是

To fix it, you need to make sure that you create a new list at each position. One way to do it is

[[1]*4 for _ in range(3)]

它将每次重新评估[1]*4而不是一次评估并且对1个列表进行3次引用.

which will reevaluate [1]*4 each time instead of evaluating it once and making 3 references to 1 list.

您可能想知道为什么*不能像列表理解那样创建独立的对象.这是因为乘法运算符*在对象上运行,而没有看到表达式.当您使用*[[1] * 4]乘以3时,*仅看到[[1] * 4]求值的1元素列表,而不是[[1] * 4表达式文本. *不知道如何制作该元素的副本,不知道如何重新评估[[1] * 4],也不知道您甚至想要复制,而且一般来说,甚至没有办法复制该元素.

You might wonder why * can't make independent objects the way the list comprehension does. That's because the multiplication operator * operates on objects, without seeing expressions. When you use * to multiply [[1] * 4] by 3, * only sees the 1-element list [[1] * 4] evaluates to, not the [[1] * 4 expression text. * has no idea how to make copies of that element, no idea how to reevaluate [[1] * 4], and no idea you even want copies, and in general, there might not even be a way to copy the element.

*唯一的选择是对现有子列表进行新引用,而不是尝试创建新子列表.其他所有内容都将不一致或需要对基础语言设计决策进行重大重新设计.

The only option * has is to make new references to the existing sublist instead of trying to make new sublists. Anything else would be inconsistent or require major redesigning of fundamental language design decisions.

相比之下,列表推导会在每次迭代时重新评估元素表达式.出于相同的原因,[[1] * 4 for n in range(3)]每次都会重新评估[1] * 4.[x**2 for x in range(3)]每次都会重新评估x**2. [1] * 4的每次评估都会生成一个新列表,因此列表理解功能可以满足您的需求.

In contrast, a list comprehension reevaluates the element expression on every iteration. [[1] * 4 for n in range(3)] reevaluates [1] * 4 every time for the same reason [x**2 for x in range(3)] reevaluates x**2 every time. Every evaluation of [1] * 4 generates a new list, so the list comprehension does what you wanted.

顺便说一句,[1] * 4也不会复制[1]的元素,但这没关系,因为整数是不可变的.您不能执行1.value = 2之类的操作并将1变成2.

Incidentally, [1] * 4 also doesn't copy the elements of [1], but that doesn't matter, since integers are immutable. You can't do something like 1.value = 2 and turn a 1 into a 2.

这篇关于列表更改列表意外地反映在子列表中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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