列表列表更改意外地反映在子列表中
2022-09-05 01:16:36
我创建了一个列表列表:
xs = [[1] * 4] * 3
# xs == [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
然后,我更改了最里面的值之一:
xs[0][0] = 5
# xs == [[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]
为什么每个子列表的第一个元素都更改为 ?5
我创建了一个列表列表:
xs = [[1] * 4] * 3
# xs == [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
然后,我更改了最里面的值之一:
xs[0][0] = 5
# xs == [[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]
为什么每个子列表的第一个元素都更改为 ?5
当你写的时候,你会得到,基本上,列表。也就是说,具有 3 个对相同 .然后,当您修改此单曲时,可以通过对它的所有三个引用来查看它:[x]*3
[x, x, x]
x
x
x = [1] * 4
xs = [x] * 3
print(f"id(x): {id(x)}")
# id(x): 140560897920048
print(
f"id(xs[0]): {id(xs[0])}\n"
f"id(xs[1]): {id(xs[1])}\n"
f"id(xs[2]): {id(xs[2])}"
)
# id(xs[0]): 140560897920048
# id(xs[1]): 140560897920048
# id(xs[2]): 140560897920048
x[0] = 42
print(f"x: {x}")
# x: [42, 1, 1, 1]
print(f"xs: {xs}")
# xs: [[42, 1, 1, 1], [42, 1, 1, 1], [42, 1, 1, 1]]
要解决此问题,您需要确保在每个位置创建一个新列表。一种方法是这样做
[[1]*4 for _ in range(3)]
每次都会重新评估,而不是评估一次,并对1个列表进行3次引用。[1]*4
您可能想知道为什么不能像列表理解那样制作独立对象。这是因为乘法运算符对对象进行操作,而看不到表达式。当您用于乘以 3 时,只看到 1 元素列表的计算结果为,而不是表达式文本。 不知道如何制作该元素的副本,不知道如何重新评估,甚至不知道你甚至想要副本,一般来说,甚至可能没有办法复制该元素。*
*
*
[[1] * 4]
*
[[1] * 4]
[[1] * 4
*
[[1] * 4]
唯一的选择是对现有子列表进行新的引用,而不是尝试创建新的子列表。其他任何内容都是不一致的,或者需要对基本语言设计决策进行重大重新设计。*
相反,列表推导在每次迭代时都会重新评估元素表达式。 每次都重新评估,原因与每次重新评估的原因相同。的每次评估都会生成一个新列表,因此列表理解可以执行所需的操作。[[1] * 4 for n in range(3)]
[1] * 4
[x**2 for x in range(3)]
x**2
[1] * 4
顺便说一句,也不会复制 的元素,但这并不重要,因为整数是不可变的。你不能做这样的事情,把1变成2。[1] * 4
[1]
1.value = 2
size = 3
matrix_surprise = [[0] * size] * size
matrix = [[0]*size for _ in range(size)]
使用Python Tutor的实时可视化: