最近在学算法,对算法进行部分修改的时候,无意间遇到了一个列表的问题,那就时列表中*
的使用,对此记录总结一下。
当我们想要生成一个嵌套的列表的时候,为了减少代码量,在初始化时,会写成如下的形式(我就是这样的)
li = [[1] * 2] * 3
print(li)
输出结果为
[[1, 1], [1, 1], [1, 1]]
而当我们想要对其中的某些子元素进行修改时,我们会天真的写成如下的形式
li[1][0] = 5
print(li)
殊不知,这将会对所有的子列表中的相应的元素进行修改,输出结果为
[[5, 1], [5, 1], [5, 1]]
其实这是因为当我们使用上面*
来创建子列表时,相当于浅拷贝(关于浅拷贝和深拷贝的相关内容不再此阐述,读者可自行查阅),当我们修改最内层的元素时,其他的子列表中的元素也会被相应的修改掉,上述代码用for
来写的话,可以写为
li_son = [1] * 2
li = []
for i in range(3):
li.append(li_son)
print(li)
li[1][0] = 5
print(li)
这边每次for
循环,添加的都是同一个对象li_son
,因此最后得到的列表相当于[li_son, li_son, li_son]
, 当我们任意修改其中一个li_son
时,对像发生了改变,因此改对象的所有变量都会发生改变。
当我们想只修改其中一个子列表中的某些元素,而不牵涉到其他的子列表的时候,我们可以在for
循环创建列表时,可对子列表进行拷贝(需要注意的时,我们这里仅考虑嵌套一层,对于嵌套多层的话,代码将会失效,读者可自行考虑使用深拷贝),由于我们只考虑一层嵌套,所以可以使用简单的list
来进行拷贝,代码如下
li_son = [1] * 2
li = []
for i in range(3):
li.append(list(li_son)) # 创建了一个副本 list(li),但是这个也是浅复制
print(li)
li[1][0] = 5
print(li)
输出结果为
[[1, 1], [1, 1], [1, 1]]
[[1, 1], [5, 1], [1, 1]]
或者使用列表推导,
li = [[1] * 2 for i in range(3)]
print(li)
li[1][0] = 5
print(li)
输出结果为
[[1, 1], [1, 1], [1, 1]]
[[1, 1], [5, 1], [1, 1]]
总结:当使用*
来初始化某些列表的时候,其是一种浅拷贝操作,在实际应用中,需考虑后面是否需要对创建的列表进行修改,若需要修改,则需使用深拷贝的操作;单层嵌套的话,最内侧可使用*
,因为对于单个列表使用*
(如 [1] * 2
),修改部分元素,不会修改其他元素。
好好学习,天天向上。