Python 子列表中意外反映的列表更改列表
我需要用Python创建一个列表列表,因此我键入了以下内容:Python 子列表中意外反映的列表更改列表,python,list,nested-lists,mutable,Python,List,Nested Lists,Mutable,我需要用Python创建一个列表列表,因此我键入了以下内容: my_list = [[1] * 4] * 3 列表如下所示: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]] [[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]] 然后我更改了其中一个最里面的值: my_list[0][0] = 5 现在,我的列表如下所示: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
my_list = [[1] * 4] * 3
列表如下所示:
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]
然后我更改了其中一个最里面的值:
my_list[0][0] = 5
现在,我的列表如下所示:
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]
这不是我想要或期望的。有人能解释一下发生了什么事,以及如何避开它吗
[[1] * 4] * 3
甚至:
[[1, 1, 1, 1]] * 3
创建一个引用内部[1,1,1,1]
三次的列表-而不是内部列表的三个副本,因此,无论何时修改列表(在任何位置),都会看到三次更改
与本例相同:
>>> 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]]
>>> a = []
>>> b = [a]
>>> b
[[]]
>>> a.append(1)
>>> b
[[1]]
这可能就没那么令人惊讶了
甚至:
[[1, 1, 1, 1]] * 3
创建一个引用内部[1,1,1,1]
三次的列表-而不是内部列表的三个副本,因此,无论何时修改列表(在任何位置),都会看到三次更改
与本例相同:
>>> 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]]
>>> a = []
>>> b = [a]
>>> b
[[]]
>>> a.append(1)
>>> b
[[1]]
当你写
[x]*3
时,你基本上得到了一个列表[x,x,x]
。也就是说,对同一x
有3个引用的列表。然后,当您修改此单个x
时,它将通过对它的所有三个引用可见:
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]]
要修复它,您需要确保在每个位置创建一个新列表。一种方法是
[[1]*4 for _ in range(3)]
它将每次重新评估[1]*4
,而不是评估一次并引用一个列表中的3个
您可能想知道为什么
*
不能像列表理解那样创建独立对象。这是因为乘法运算符*
对对象进行操作,而看不到表达式。当您使用*
将[[1]*4]
乘以3时,*
只会看到1元素列表[[1]*4]
的计算结果,而不是[[1]*4
表达式文本。*
不知道如何复制该元素,也不知道如何重新计算[[1]*4]
,甚至不知道您想要复制,而且通常,甚至可能没有复制元素的方法
*
唯一的选择是对现有子列表进行新的引用,而不是尝试创建新的子列表。其他任何内容都将不一致,或者需要对基本语言设计决策进行重大重新设计
相反,列表理解在每次迭代时都会重新评估元素表达式。[[1]*4代表范围(3)中的n]
每次都会重新评估[1]*4
,原因相同[x**2代表范围(3)]
每次都会重新评估x**2
*4生成一个新列表,因此列表理解功能可以满足您的需要
顺便说一句,
[1]*4
也不会复制[1]
的元素,但这并不重要,因为整数是不可变的。你不能像1.value=2
那样把1变成2。当你写[x]*3
时,你基本上得到的是列表[x,x,x]
。也就是说,一个列表包含对同一个x
的三个引用。然后修改此x
时,通过对它的所有三个引用可见:
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]]
要修复它,您需要确保在每个位置创建一个新列表
[[1]*4 for _ in range(3)]
它将每次重新评估[1]*4
,而不是评估一次并引用一个列表中的3个
您可能想知道为什么
*
不能像列表理解那样生成独立的对象。这是因为乘法运算符*
对对象进行运算,而看不到表达式。当您使用*
将[[1]*4]
乘以3时,*
只看到1元素列表[[1]*4]
的计算结果是,而不是[[1]*4
表达式文本。*
不知道如何复制该元素,不知道如何重新计算[[1]*4]
,甚至不知道您想要复制,而且一般来说,甚至可能没有复制该元素的方法
*
唯一的选择是对现有子列表进行新的引用,而不是尝试创建新的子列表。其他任何内容都将不一致,或者需要对基本语言设计决策进行重大重新设计
相反,列表理解在每次迭代时都会重新评估元素表达式。[[1]*4代表范围(3)中的n]
每次都会重新评估[1]*4
,原因相同[x**2代表范围(3)]
每次都会重新评估x**2
*4生成一个新列表,因此列表理解功能可以满足您的需要
顺便说一句,
[1]*4
也不会复制[1]
的元素,但这并不重要,因为整数是不可变的。你不能像1.value=2
那样把1变成2。实际上,这正是你所期望的。让我们来分解一下这里发生的事情:
你写
lst = [[1] * 4] * 3
这相当于:
lst1 = [1]*4
lst = [lst1]*3
这意味着lst
是一个包含3个元素的列表,所有元素都指向lst1
。这意味着以下两行是等效的:
lst[0][0] = 5
lst1[0] = 5
Aslst[0]
不过是lst1
要获得所需的行为,可以使用列表:
lst = [ [1]*4 for n in range(3) ]
在本例中,表达式将针对每个
n
重新求值,从而生成一个不同的列表。实际上,这正是您所期望的。让我们分解这里发生的事情:
size = 3
matrix_surprise = [[0] * size] * size
matrix = [[0]*size for _ in range(size)]
你写
lst = [[1] * 4] * 3
这相当于:
lst1 = [1]*4
lst = [lst1]*3
这意味着lst
是一个包含3个元素的列表,所有元素都指向lst1
。这意味着以下两行是等效的:
lst[0][0] = 5
lst1[0] = 5
Aslst[0]
不过是lst1
要获得所需的行为,可以使用列表:
lst = [ [1]*4 for n in range(3) ]
在这种情况下,将针对每个n
重新计算表达式,从而生成不同的列表
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]]
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]]
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]]
my_list = [[1]*4 for _ in range(3)]
my_list = [[1 for __ in range(4)] for _ in range(3)]
>>> 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
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)]
>>> lists = [[]] * 3
>>> lists
[[], [], []]
>>> lists[0].append(3)
>>> lists
[[3], [3], [3]]
>>> A
[[None, None], [None, None], [None, None]]
>>> A[0][0] = 5
>>> A
[[5, None], [5, None], [5, None]]
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]}