列表列表更改意外地反映在子列表中

发布于 2024-07-08 03:27:19 字数 793 浏览 7 评论 0原文

我创建了一个列表列表:

>>> xs = [[1] * 4] * 3
>>> print(xs)
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]

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

>>> xs[0][0] = 5
>>> print(xs)
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]

为什么每个子列表的每个第一个元素都更改为 5


另请参阅:

I created a list of lists:

>>> xs = [[1] * 4] * 3
>>> print(xs)
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]

Then, I changed one of the innermost values:

>>> xs[0][0] = 5
>>> print(xs)
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]

Why did every first element of each sublist change to 5?


See also:

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(19

前事休说 2024-07-15 03:27:19

当您编写 [x]*3 时,您实质上会得到列表 [x, x, x]。 也就是说,一个列表包含 3 个对同一 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]*4,而不是评估一次并对 1 个列表进行 3 次引用。


您可能想知道为什么 * 不能像列表理解那样创建独立的对象。 这是因为乘法运算符 * 对对象进行运算,而看不到表达式。 当您使用 *[[1] * 4] 乘以 3 时,* 只能看到 1 元素列表 [[ 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] * 4 也不会复制 [1] 的元素,但这并不重要,因为整数是不可变的。 您不能执行诸如 1.value = 2 之类的操作,将 1 转换为 2。

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
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]]

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)]

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


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.

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.

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.

佼人 2024-07-15 03:27:19
size = 3
matrix_surprise = [[0] * size] * size
matrix = [[0]*size for _ in range(size)]

使用 Python Tutor 进行实时可视化

框架和对象

size = 3
matrix_surprise = [[0] * size] * size
matrix = [[0]*size for _ in range(size)]

Live visualization using Python Tutor:

Frames and Objects

月下客 2024-07-15 03:27:19

事实上,这正是您所期望的。 让我们分解一下这里发生的事情:

你这样写

lst = [[1] * 4] * 3

这相当于:

lst1 = [1]*4
lst = [lst1]*3

这意味着 lst 是一个包含 3 个元素的列表,全部指向 lst1。 这意味着以下两行是等效的:

lst[0][0] = 5
lst1[0] = 5

因为lst[0]只不过是lst1

要获得所需的行为,您可以使用列表理解:

lst = [ [1]*4 for n in range(3) ]

在这种情况下,将针对每个 n 重新计算表达式,从而生成不同的列表。

Actually, this is exactly what you would expect. Let's decompose what is happening here:

You write

lst = [[1] * 4] * 3

This is equivalent to:

lst1 = [1]*4
lst = [lst1]*3

This means lst is a list with 3 elements all pointing to lst1. This means the two following lines are equivalent:

lst[0][0] = 5
lst1[0] = 5

As lst[0] is nothing but lst1.

To obtain the desired behavior, you can use a list comprehension:

lst = [ [1]*4 for n in range(3) ]

In this case, the expression is re-evaluated for each n, leading to a different list.

流年已逝 2024-07-15 03:27:19
[[1] * 4] * 3

甚至:

[[1, 1, 1, 1]] * 3

创建一个引用内部 [1,1,1,1] 3 次的列表 - 不是内部列表的三个副本,因此任何时候您修改列表(在任何位置),您将看到三次更改。

它与这个例子是一样的:

>>> inner = [1,1,1,1]
>>> outer = [inner]*3
>>> outer
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
>>> inner[0] = 5
>>> outer
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]

它可能不那么令人惊讶。

[[1] * 4] * 3

or even:

[[1, 1, 1, 1]] * 3

Creates a list that references the internal [1,1,1,1] 3 times - not three copies of the inner list, so any time you modify the list (in any position), you'll see the change three times.

It's the same as this example:

>>> inner = [1,1,1,1]
>>> outer = [inner]*3
>>> outer
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
>>> inner[0] = 5
>>> outer
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]

where it's probably a little less surprising.

十二 2024-07-15 03:27:19

my_list = [[1]*4] * 3 在内存中创建一个列表对象[1,1,1,1],并复制其引用 3 次。 这相当于 obj = [1,1,1,1]; my_list = [obj]*3. 对 obj 的任何修改都将反映在列表中引用 obj 的三个位置。
正确的说法是:

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

或者

my_list = [[1 for __ in range(4)] for _ in range(3)]

这里要注意的重要事情*运算符主要用于创建文字列表。 尽管 1 是不可变的,obj = [1]*4 仍会创建重复 4 次的 1 列表以形成 [ 1,1,1,1]。 但是,如果对不可变对象进行任何引用,该对象就会被新对象覆盖。

这意味着如果我们执行 obj[1] = 42,那么 obj 将变为 [1,42,1,1] [42,42,42,42] 正如某些人可能假设的那样。 这也可以验证:

>>> my_list = [1]*4
>>> my_list
[1, 1, 1, 1]

>>> id(my_list[0])
4522139440
>>> id(my_list[1])  # Same as my_list[0]
4522139440

>>> my_list[1] = 42  # Since my_list[1] is immutable, this operation overwrites my_list[1] with a new object changing its id.
>>> my_list
[1, 42, 1, 1]

>>> id(my_list[0])
4522139440
>>> id(my_list[1])  # id changed
4522140752
>>> id(my_list[2])  # id still same as my_list[0], still referring to value `1`.
4522139440

my_list = [[1]*4] * 3 creates one list object [1,1,1,1] in memory and copies its reference 3 times over. This is equivalent to obj = [1,1,1,1]; my_list = [obj]*3. Any modification to obj will be reflected at three places, wherever obj is referenced in the list.
The right statement would be:

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

or

my_list = [[1 for __ in range(4)] for _ in range(3)]

Important thing to note here is that the * operator is mostly used to create a list of literals. Although 1 is immutable, obj = [1]*4 will still create a list of 1 repeated 4 times over to form [1,1,1,1]. But if any reference to an immutable object is made, the object is overwritten with a new one.

This means if we do obj[1] = 42, then obj will become [1,42,1,1] not [42,42,42,42] as some may assume. This can also be verified:

>>> my_list = [1]*4
>>> my_list
[1, 1, 1, 1]

>>> id(my_list[0])
4522139440
>>> id(my_list[1])  # Same as my_list[0]
4522139440

>>> my_list[1] = 42  # Since my_list[1] is immutable, this operation overwrites my_list[1] with a new object changing its id.
>>> my_list
[1, 42, 1, 1]

>>> id(my_list[0])
4522139440
>>> id(my_list[1])  # id changed
4522140752
>>> id(my_list[2])  # id still same as my_list[0], still referring to value `1`.
4522139440
木格 2024-07-15 03:27:19

除了正确解释问题的接受答案之外,不要使用以下代码创建包含重复元素的列表:

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

此外,您可以使用 itertools.repeat() 创建重复元素的迭代器对象:

>>> a = list(repeat(1,4))
[1, 1, 1, 1]
>>> a[0] = 5
>>> a
[5, 1, 1, 1]

PS 如果您使用 NumPy 并且只想创建一个您可以使用的一或零数组 np.onesnp.zeros 和/或对于其他数字,请使用 np .重复

>>> import numpy as np
>>> np.ones(4)
array([1., 1., 1., 1.])
>>> np.ones((4, 2))
array([[1., 1.],
       [1., 1.],
       [1., 1.],
       [1., 1.]])
>>> np.zeros((4, 2))
array([[0., 0.],
       [0., 0.],
       [0., 0.],
       [0., 0.]])
>>> np.repeat([7], 10)
array([7, 7, 7, 7, 7, 7, 7, 7, 7, 7])

Alongside the accepted answer that explained the problem correctly, instead of creating a list with duplicated elements using following code:

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

Also, you can use itertools.repeat() to create an iterator object of repeated elements:

>>> a = list(repeat(1,4))
[1, 1, 1, 1]
>>> a[0] = 5
>>> a
[5, 1, 1, 1]

P.S. If you're using NumPy and you only want to create an array of ones or zeroes you can use np.ones and np.zeros and/or for other numbers use np.repeat:

>>> import numpy as np
>>> np.ones(4)
array([1., 1., 1., 1.])
>>> np.ones((4, 2))
array([[1., 1.],
       [1., 1.],
       [1., 1.],
       [1., 1.]])
>>> np.zeros((4, 2))
array([[0., 0.],
       [0., 0.],
       [0., 0.],
       [0., 0.]])
>>> np.repeat([7], 10)
array([7, 7, 7, 7, 7, 7, 7, 7, 7, 7])
橘虞初梦 2024-07-15 03:27:19

Python 容器包含对其他对象的引用。 请参阅此示例:

>>> a = []
>>> b = [a]
>>> b
[[]]
>>> a.append(1)
>>> b
[[1]]

在此 b 中,是一个列表,其中包含一项对列表 a 的引用。 列表a是可变的。

列表乘以整数相当于多次将列表与自身相加(请参阅 常见序列操作)。 继续这个例子:

>>> c = b + b
>>> c
[[1], [1]]
>>>
>>> a[0] = 2
>>> c
[[2], [2]]

我们可以看到列表c现在包含两个对列表a的引用,这相当于c = b * 2

Python FAQ 还包含对此行为的解释: How do I create a multiDimensional列表?

Python containers contain references to other objects. See this example:

>>> a = []
>>> b = [a]
>>> b
[[]]
>>> a.append(1)
>>> b
[[1]]

In this b is a list that contains one item that is a reference to list a. The list a is mutable.

The multiplication of a list by an integer is equivalent to adding the list to itself multiple times (see common sequence operations). So continuing with the example:

>>> c = b + b
>>> c
[[1], [1]]
>>>
>>> a[0] = 2
>>> c
[[2], [2]]

We can see that the list c now contains two references to list a which is equivalent to c = b * 2.

Python FAQ also contains explanation of this behavior: How do I create a multidimensional list?

心凉 2024-07-15 03:27:19

我添加我的答案以图解方式解释相同的内容。

创建 2D 的方式创建了一个浅列表

arr = [[0]*cols]*row

相反,如果您想更新列表的元素,您应该使用

rows, cols = (5, 5) 
arr = [[0 for i in range(cols)] for j in range(rows)] 

解释

可以使用以下命令创建列表:

arr = [0]*N 

arr = [0 for i in range(N)] 

在第一种情况下,所有数组的索引指向同一个整数对象

在此处输入图像描述

当您为特定索引分配值时,会创建一个新的 int 对象,例如 arr[4] = 5 创建

在此处输入图像描述

现在让我们看看当我们创建一个列表列表时会发生什么,在这种情况下,顶部列表的所有元素都将指向同一个列表

在此处输入图像描述

并且如果更新任何索引的值,将创建一个新的 int 对象。 但由于所有顶级列表索引都指向同一个列表,因此所有行看起来都一样。 你会感觉更新一个元素就是更新该列中的所有元素。

输入图片此处描述

鸣谢:感谢Pranav Devarakonda 的简单解释这里< /a>

I am adding my answer to explain the same diagrammatically.

The way you created the 2D, creates a shallow list

arr = [[0]*cols]*row

Instead, if you want to update the elements of the list, you should use

rows, cols = (5, 5) 
arr = [[0 for i in range(cols)] for j in range(rows)] 

Explanation:

One can create a list using:

arr = [0]*N 

or

arr = [0 for i in range(N)] 

In the first case all the indices of the array point to the same integer object

enter image description here

and when you assign a value to a particular index, a new int object is created, for example arr[4] = 5 creates

enter image description here

Now let us see what happens when we create a list of list, in this case, all the elements of our top list will point to the same list

enter image description here

And if you update the value of any index a new int object will be created. But since all the top-level list indexes are pointing at the same list, all the rows will look the same. And you will get the feeling that updating an element is updating all the elements in that column.

enter image description here

Credits: Thanks to Pranav Devarakonda for the easy explanation here

感性不性感 2024-07-15 03:27:19

让我们按以下方式重写您的代码:

x = 1
y = [x]
z = y * 4

my_list = [z] * 3

然后运行以下代码以使一切更加清晰。 代码的作用基本上是打印 ids,其中

返回对象的“身份”

,将帮助我们识别它们并分析发生的情况:

print("my_list:")
for i, sub_list in enumerate(my_list):
    print("\t[{}]: {}".format(i, id(sub_list)))
    for j, elem in enumerate(sub_list):
        print("\t\t[{}]: {}".format(j, id(elem)))

您将得到以下输出:

x: 1
y: [1]
z: [1, 1, 1, 1]
my_list:
    [0]: 4300763792
        [0]: 4298171528
        [1]: 4298171528
        [2]: 4298171528
        [3]: 4298171528
    [1]: 4300763792
        [0]: 4298171528
        [1]: 4298171528
        [2]: 4298171528
        [3]: 4298171528
    [2]: 4300763792
        [0]: 4298171528
        [1]: 4298171528
        [2]: 4298171528
        [3]: 4298171528

现在让我们一步一步进行。 您有 x(即 1),以及包含 x 的单个元素列表 y。 你的第一步是y * 4,它将为你提供一个新的列表z,它基本上是[x, x, x, x],即它创建一个新列表,其中包含 4 个元素,这些元素是对初始 x 对象的引用。 下一步非常相似。 您基本上执行 z * 3,即 [[x, x, x, x]] * 3 并返回 [[x, x, x, x ], [x, x, x, x], [x, x, x, x]],原因与第一步相同。

Let's rewrite your code in the following way:

x = 1
y = [x]
z = y * 4

my_list = [z] * 3

Then having this, run the following code to make everything more clear. What the code does is basically print the ids of the obtained objects, which

Return[s] the “identity” of an object

and will help us identify them and analyse what happens:

print("my_list:")
for i, sub_list in enumerate(my_list):
    print("\t[{}]: {}".format(i, id(sub_list)))
    for j, elem in enumerate(sub_list):
        print("\t\t[{}]: {}".format(j, id(elem)))

And you will get the following output:

x: 1
y: [1]
z: [1, 1, 1, 1]
my_list:
    [0]: 4300763792
        [0]: 4298171528
        [1]: 4298171528
        [2]: 4298171528
        [3]: 4298171528
    [1]: 4300763792
        [0]: 4298171528
        [1]: 4298171528
        [2]: 4298171528
        [3]: 4298171528
    [2]: 4300763792
        [0]: 4298171528
        [1]: 4298171528
        [2]: 4298171528
        [3]: 4298171528

So now let's go step-by-step. You have x which is 1, and a single element list y containing x. Your first step is y * 4 which will get you a new list z, which is basically [x, x, x, x], i.e. it creates a new list which will have 4 elements, which are references to the initial x object. The next step is pretty similar. You basically do z * 3, which is [[x, x, x, x]] * 3 and returns [[x, x, x, x], [x, x, x, x], [x, x, x, x]], for the same reason as for the first step.

清旖 2024-07-15 03:27:19

简单来说,发生这种情况是因为在 python 中,一切都通过引用工作,所以当你创建一个列表列表时,你基本上会遇到这样的问题。

要解决您的问题,您可以执行以下任一操作:

  1. 使用 numpy array; numpy.empty 文档
  2. 追加当您到达列表时,该列表。
  3. 如果需要,您也可以使用字典

In simple words this is happening because in python everything works by reference, so when you create a list of list that way you basically end up with such problems.

To solve your issue you can do either one of them:

  1. Use numpy array; documentation for numpy.empty
  2. Append the list as you get to a list.
  3. You can also use dictionary if you want
眼泪都笑了 2024-07-15 03:27:19

每个人都在解释发生了什么。 我会建议一种解决方法:

my_list = [[1 for i in range(4)] for j in range(3)]

my_list[0][0] = 5
print(my_list)

然后你会得到:

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

Everyone is explaining what is happening. I'll suggest one way to solve it:

my_list = [[1 for i in range(4)] for j in range(3)]

my_list[0][0] = 5
print(my_list)

And then you get:

[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
浪漫人生路 2024-07-15 03:27:19

@spelchekr 来自 Python 列表乘法:[ [...]]*3 制作了 3 个列表,修改后彼此镜像,我也有同样的问题
“为什么只有外部 *3 会创建更多引用,而内部却不会?为什么不都是 1?”

li = [0] * 3
print([id(v) for v in li])  # [140724141863728, 140724141863728, 140724141863728]
li[0] = 1
print([id(v) for v in li])  # [140724141863760, 140724141863728, 140724141863728]
print(id(0))  # 140724141863728
print(id(1))  # 140724141863760
print(li)     # [1, 0, 0]

ma = [[0]*3] * 3  # mainly discuss inner & outer *3 here
print([id(li) for li in ma])  # [1987013355080, 1987013355080, 1987013355080]
ma[0][0] = 1
print([id(li) for li in ma])  # [1987013355080, 1987013355080, 1987013355080]
print(ma)  # [[1, 0, 0], [1, 0, 0], [1, 0, 0]]

这是我在尝试上面的代码后的解释:

  • 内部 *3 也会创建引用,但它的引用是不可变的,类似于 [&0, &0, &0]< /code>,那么当你改变li[0]时,你无法改变const int 0的任何底层引用,所以你只需将引用地址改为新的 &1;
  • ma = [&li, &li, &li]li 是可变的,所以当你调用 ma[0][0] = 1ma[0][0]等于&li[0],所以所有的&li实例会将其第一个地址更改为 &1

@spelchekr from Python list multiplication: [[...]]*3 makes 3 lists which mirror each other when modified and I had the same question about
"Why does only the outer *3 create more references while the inner one doesn't? Why isn't it all 1s?"

li = [0] * 3
print([id(v) for v in li])  # [140724141863728, 140724141863728, 140724141863728]
li[0] = 1
print([id(v) for v in li])  # [140724141863760, 140724141863728, 140724141863728]
print(id(0))  # 140724141863728
print(id(1))  # 140724141863760
print(li)     # [1, 0, 0]

ma = [[0]*3] * 3  # mainly discuss inner & outer *3 here
print([id(li) for li in ma])  # [1987013355080, 1987013355080, 1987013355080]
ma[0][0] = 1
print([id(li) for li in ma])  # [1987013355080, 1987013355080, 1987013355080]
print(ma)  # [[1, 0, 0], [1, 0, 0], [1, 0, 0]]

Here is my explanation after trying the code above:

  • The inner *3 also creates references, but its references are immutable, something like [&0, &0, &0], then when you change li[0], you can't change any underlying reference of const int 0, so you can just change the reference address into the new one &1;
  • while ma = [&li, &li, &li] and li is mutable, so when you call ma[0][0] = 1, ma[0][0] is equal to &li[0], so all the &li instances will change its 1st address into &1.
时光沙漏 2024-07-15 03:27:19

尝试更描述性地解释它,

操作 1:

x = [[0, 0], [0, 0]]
print(type(x)) # <class 'list'>
print(x) # [[0, 0], [0, 0]]

x[0][0] = 1
print(x) # [[1, 0], [0, 0]]

操作 2:

y = [[0] * 2] * 2
print(type(y)) # <class 'list'>
print(y) # [[0, 0], [0, 0]]

y[0][0] = 1
print(y) # [[1, 0], [1, 0]]

注意到为什么修改第一个列表的第一个元素没有修改每个列表的第二个元素? 这是因为 [0] * 2 实际上是两个数字的列表,并且无法修改对 0 的引用。

如果您想创建克隆副本,请尝试操作 3:

import copy
y = [0] * 2   
print(y)   # [0, 0]

y = [y, copy.deepcopy(y)]  
print(y) # [[0, 0], [0, 0]]

y[0][0] = 1
print(y) # [[1, 0], [0, 0]]

创建克隆副本的另一种有趣方法,操作 4:

import copy
y = [0] * 2
print(y) # [0, 0]

y = [copy.deepcopy(y) for num in range(1,5)]
print(y) # [[0, 0], [0, 0], [0, 0], [0, 0]]

y[0][0] = 5
print(y) # [[5, 0], [0, 0], [0, 0], [0, 0]]

Trying to explain it more descriptively,

Operation 1:

x = [[0, 0], [0, 0]]
print(type(x)) # <class 'list'>
print(x) # [[0, 0], [0, 0]]

x[0][0] = 1
print(x) # [[1, 0], [0, 0]]

Operation 2:

y = [[0] * 2] * 2
print(type(y)) # <class 'list'>
print(y) # [[0, 0], [0, 0]]

y[0][0] = 1
print(y) # [[1, 0], [1, 0]]

Noticed why doesn't modifying the first element of the first list didn't modify the second element of each list? That's because [0] * 2 really is a list of two numbers, and a reference to 0 cannot be modified.

If you want to create clone copies, try Operation 3:

import copy
y = [0] * 2   
print(y)   # [0, 0]

y = [y, copy.deepcopy(y)]  
print(y) # [[0, 0], [0, 0]]

y[0][0] = 1
print(y) # [[1, 0], [0, 0]]

another interesting way to create clone copies, Operation 4:

import copy
y = [0] * 2
print(y) # [0, 0]

y = [copy.deepcopy(y) for num in range(1,5)]
print(y) # [[0, 0], [0, 0], [0, 0], [0, 0]]

y[0][0] = 5
print(y) # [[5, 0], [0, 0], [0, 0], [0, 0]]
毅然前行 2024-07-15 03:27:19

通过使用内置列表功能,您可以这样做

a
out:[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
#Displaying the list

a.remove(a[0])
out:[[1, 1, 1, 1], [1, 1, 1, 1]]
# Removed the first element of the list in which you want altered number

a.append([5,1,1,1])
out:[[1, 1, 1, 1], [1, 1, 1, 1], [5, 1, 1, 1]]
# append the element in the list but the appended element as you can see is appended in last but you want that in starting

a.reverse()
out:[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
#So at last reverse the whole list to get the desired list

By using the inbuilt list function you can do like this

a
out:[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
#Displaying the list

a.remove(a[0])
out:[[1, 1, 1, 1], [1, 1, 1, 1]]
# Removed the first element of the list in which you want altered number

a.append([5,1,1,1])
out:[[1, 1, 1, 1], [1, 1, 1, 1], [5, 1, 1, 1]]
# append the element in the list but the appended element as you can see is appended in last but you want that in starting

a.reverse()
out:[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
#So at last reverse the whole list to get the desired list
旧伤还要旧人安 2024-07-15 03:27:19

来自官方文档

请注意,
序列 s 不会被复制; 它们被多次引用。
这常常困扰着新的 Python 程序员; 考虑::

<前><代码>>>>> 列表 = [[]] * 3
>>>>> 列表
[[]、[]、[]]
>>>>> 列表[0].append(3)
>>>>> 列表
[[3]、[3]、[3]]

发生的事情是 [[]] 是一个单元素列表,其中包含
一个空列表,因此 [[]] * 3 的所有三个元素都是引用
到这个空列表。 修改任何元素
lists 修改此单个列表。 您可以创建一个列表
这样不同的列表::

<前><代码>>>>> 列表 = [[] for i in range(3)]
>>>>> 列表[0].append(3)
>>>>> 列表[1].append(5)
>>>>> 列表[2].append(7)
>>>>> 列表
[[3]、[5]、[7]]

From official documentation:

Note that items in the
sequence s are not copied; they are referenced multiple times.
This often haunts new Python programmers; consider::

  >>> lists = [[]] * 3
  >>> lists
  [[], [], []]
  >>> lists[0].append(3)
  >>> lists
  [[3], [3], [3]]

What has happened is that [[]] is a one-element list containing
an empty list, so all three elements of [[]] * 3 are references
to this single empty list. Modifying any of the elements of
lists modifies this single list. You can create a list of
different lists this way::

  >>> lists = [[] for i in range(3)]
  >>> lists[0].append(3)
  >>> lists[1].append(5)
  >>> lists[2].append(7)
  >>> lists
  [[3], [5], [7]]
橘虞初梦 2024-07-15 03:27:19

我来到这里是因为我想看看如何嵌套任意数量的列表。 上面有很多解释和具体示例,但是您可以使用以下递归函数概括 N 维列表的列表的列表...:

import copy

def list_ndim(dim, el=None, init=None):
    if init is None:
        init = el

    if len(dim)> 1:
        return list_ndim(dim[0:-1], None, [copy.copy(init) for x in range(dim[-1])])

    return [copy.deepcopy(init) for x in range(dim[0])]

您第一次调用该函数,如下所示:

dim = (3,5,2)
el = 1.0
l = list_ndim(dim, el)

where (3, 5,2) 是结构维度的元组(类似于 numpy shape 参数),1.0 是您希望结构成为的元素初始化为(也适用于 None)。 请注意, init 参数仅由递归调用提供,以继承

上面的嵌套子列表输出:

[[[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]],
 [[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]],
 [[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]]]

设置特定元素:

l[1][3][1] = 56
l[2][2][0] = 36.0+0.0j
l[0][1][0] = 'abc'

结果输出:

[[[1.0, 1.0], ['abc', 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]],
 [[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 56.0], [1.0, 1.0]],
 [[1.0, 1.0], [1.0, 1.0], [(36+0j), 1.0], [1.0, 1.0], [1.0, 1.0]]]

上面演示了列表的非类型化本质

I arrived here because I was looking to see how I could nest an arbitrary number of lists. There are a lot of explanations and specific examples above, but you can generalize N dimensional list of lists of lists of ... with the following recursive function:

import copy

def list_ndim(dim, el=None, init=None):
    if init is None:
        init = el

    if len(dim)> 1:
        return list_ndim(dim[0:-1], None, [copy.copy(init) for x in range(dim[-1])])

    return [copy.deepcopy(init) for x in range(dim[0])]

You make your first call to the function like this:

dim = (3,5,2)
el = 1.0
l = list_ndim(dim, el)

where (3,5,2) is a tuple of the dimensions of the structure (similar to numpy shape argument), and 1.0 is the element you want the structure to be initialized with (works with None as well). Note that the init argument is only provided by the recursive call to carry forward the nested child lists

output of above:

[[[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]],
 [[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]],
 [[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]]]

set specific elements:

l[1][3][1] = 56
l[2][2][0] = 36.0+0.0j
l[0][1][0] = 'abc'

resulting output:

[[[1.0, 1.0], ['abc', 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]],
 [[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 56.0], [1.0, 1.0]],
 [[1.0, 1.0], [1.0, 1.0], [(36+0j), 1.0], [1.0, 1.0], [1.0, 1.0]]]

the non-typed nature of lists is demonstrated above

‘画卷フ 2024-07-15 03:27:19

虽然最初的问题使用乘法运算符构造了子列表,但我将添加一个使用相同子列表列表的示例。 为了完整性添加这个答案,因为这个问题经常被用作该问题的规范。

node_count = 4
colors = [0,1,2,3]
sol_dict = {node:colors for node in range(0,node_count)}

每个字典值中的列表都是同一个对象,尝试更改其中一个字典值将在所有中看到。

>>> sol_dict
{0: [0, 1, 2, 3], 1: [0, 1, 2, 3], 2: [0, 1, 2, 3], 3: [0, 1, 2, 3]}
>>> [v is colors for v in sol_dict.values()]
[True, True, True, True]
>>> sol_dict[0].remove(1)
>>> sol_dict
{0: [0, 2, 3], 1: [0, 2, 3], 2: [0, 2, 3], 3: [0, 2, 3]}

构建字典的正确方法是为每个值使用列表的副本。

>>> colors = [0,1,2,3]
>>> sol_dict = {node:colors[:] for node in range(0,node_count)}
>>> sol_dict
{0: [0, 1, 2, 3], 1: [0, 1, 2, 3], 2: [0, 1, 2, 3], 3: [0, 1, 2, 3]}
>>> sol_dict[0].remove(1)
>>> sol_dict
{0: [0, 2, 3], 1: [0, 1, 2, 3], 2: [0, 1, 2, 3], 3: [0, 1, 2, 3]}

While the original question constructed the sublists with the multiplication operator, I'll add an example that uses the same list for the sublists. Adding this answer for completeness as this question is often used as a canonical for the issue

node_count = 4
colors = [0,1,2,3]
sol_dict = {node:colors for node in range(0,node_count)}

The list in each dictionary value is the same object, trying to change one of the dictionaries values will be seen in all.

>>> sol_dict
{0: [0, 1, 2, 3], 1: [0, 1, 2, 3], 2: [0, 1, 2, 3], 3: [0, 1, 2, 3]}
>>> [v is colors for v in sol_dict.values()]
[True, True, True, True]
>>> sol_dict[0].remove(1)
>>> sol_dict
{0: [0, 2, 3], 1: [0, 2, 3], 2: [0, 2, 3], 3: [0, 2, 3]}

The correct way to construct the dictionary would be to use a copy of the list for each value.

>>> colors = [0,1,2,3]
>>> sol_dict = {node:colors[:] for node in range(0,node_count)}
>>> sol_dict
{0: [0, 1, 2, 3], 1: [0, 1, 2, 3], 2: [0, 1, 2, 3], 3: [0, 1, 2, 3]}
>>> sol_dict[0].remove(1)
>>> sol_dict
{0: [0, 2, 3], 1: [0, 1, 2, 3], 2: [0, 1, 2, 3], 3: [0, 1, 2, 3]}
不奢求什么 2024-07-15 03:27:19

请注意,序列中的项目不会被复制; 它们被多次引用。 这常常困扰着新的 Python 程序员; 考虑:

>>> lists = [[]] * 3
>>> lists
[[], [], []]
>>> lists[0].append(3)
>>> lists
[[3], [3], [3]]

发生的情况是 [[]] 是一个包含空列表的单元素列表,因此 [[]] * 3 的所有三个元素都是对这个空列表。 修改列表的任何元素都会修改该单个列表。

解释这一点的另一个例子是使用多维数组

您可能尝试创建一个像这样的多维数组:

>>> A = [[None] * 2] * 3

如果打印它,这看起来是正确的:

>>> A
[[None, None], [None, None], [None, None]]

但是当您分配一个值时,它会出现在多个位置:

>>> A[0][0] = 5
>>> A
[[5, None], [5, None], [5, None]]

原因是使用 * 复制列表 不创建副本,它只创建对现有对象的引用。 3 创建一个列表,其中包含对长度为 2 的同一列表的 3 个引用。 对一行的更改将显示在所有行中,这几乎肯定不是您想要的。

Note that items in the sequence are not copied; they are referenced multiple times. This often haunts new Python programmers; consider:

>>> lists = [[]] * 3
>>> lists
[[], [], []]
>>> lists[0].append(3)
>>> lists
[[3], [3], [3]]

What has happened is that [[]] is a one-element list containing an empty list, so all three elements of [[]] * 3 are references to this single empty list. Modifying any of the elements of lists modifies this single list.

Another example to explain this is using multi-dimensional arrays.

You probably tried to make a multidimensional array like this:

>>> A = [[None] * 2] * 3

This looks correct if you print it:

>>> A
[[None, None], [None, None], [None, None]]

But when you assign a value, it shows up in multiple places:

>>> A[0][0] = 5
>>> A
[[5, None], [5, None], [5, None]]

The reason is that replicating a list with * doesn’t create copies, it only creates references to the existing objects. The 3 creates a list containing 3 references to the same list of length two. Changes to one row will show in all rows, which is almost certainly not what you want.

窝囊感情。 2024-07-15 03:27:19

每个子列表的每个第一个元素都更改为 5,因为“xs”具有相同的列表 3 次,数据正在共享。 打印不显示这一点,但使用:

https://pypi.org/project/memory-graph/

您可以绘制数据图表并轻松查看:

import memory_graph # see install instructions at link above

xs = [[1] * 4] * 3
print(xs)
xs[0][0] = 5
print(xs)

memory_graph.d() # draw graph

在此处输入图像描述

相反,您可能希望使用列表理解,以便不共享数据:

import memory_graph

xs = [[1] * 4 for _ in range(3)] # <----- list comprehension
print(xs)
xs[0][0] = 5
print(xs)

memory_graph.d()

在此处输入图像描述

给出输出:

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

全面披露:我是 memory_graph 的开发者。

Every first element of each sublist changed to 5 because 'xs' has the same list three times, the data is being shared. Printing doesn't show this but using:

https://pypi.org/project/memory-graph/

you can graph your data and see this easily:

import memory_graph # see install instructions at link above

xs = [[1] * 4] * 3
print(xs)
xs[0][0] = 5
print(xs)

memory_graph.d() # draw graph

enter image description here

Instead you probably want to use a list comprehension so that the data is not shared:

import memory_graph

xs = [[1] * 4 for _ in range(3)] # <----- list comprehension
print(xs)
xs[0][0] = 5
print(xs)

memory_graph.d()

enter image description here

Which gives output:

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

Full disclosure: I am the developer of memory_graph.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文