关于Python列表的一道题

  • 前因后果
  • 做题思路
  • 流程
  • 代码
  • 调试
  • 思考和猜想
  • append()函数
  • 解决方法
  • 修改后的代码
  • 小结

前因后果

题目:list1=[1,2,3,4],怎么做才能得到 [2,3,4],[1,3,4],[1,2,4],[1,2,3]

做题思路

观察原列表 list1 和 目标列表,是将原列表的每一个元素都删除一次后,添加到目标列表中去。
且每次原列表删除的元素位置,和目标列表添加新列表的位置 是一致的。
有点绕口,表达的不是很好。
意思就是:
第一次,原列表删除 0 号元素,变成[2,3,4],将[2,3,4]添加到目标列表的 0 号位置。
第二次,原列表删除 1 号元素,变成[1,3,4],将[1,3,4]添加到目标列表的 1 号位置。
以此类推

流程

1、依次遍历原列表list1的每一个元素each
2、记录当次遍历到的元素所在的下标index
3、在list1中删除下标为index的元素
4、将删除元素后的list1添加到目标列表list2的末尾
5、恢复list1,方便下一次循环时重新使用

代码

#原列表
list1 = [1, 2, 3, 4]

#目标列表
list2 = []

#依次遍历原列表list1的每一个元素each
for each in list1:

    #记录当次遍历到的元素所在的下标index
    index = list1.index(each)

    #在list1中删除下标为index的元素
    list1.pop(index)

    #将删除元素后的list1添加到目标列表list2的末尾
    list2.append(list1)

    #恢复list1,方便下一次循环时重新使用
    list1.insert(index,each)

print(list2)

运行结果:

[[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]
显然结果是不对的,但是逻辑上暂时找不出什么问题

调试

当我们满怀信心地写代码,最后结果却和我们预期不符时,就需要用到Debug工具了,在程序的每一个关键位置打上断点,观察哪里出了问题

python的列表练习题_python的列表练习题


这里我们发现,第一次循环到这里为止都按照我们预期地进行,那么我们继续按F8下一步:

python的列表练习题_Python_02


问题来了,走出第一个循环之前,我们恢复了list1的内容,以便接下来的循环还可以继续使用,但是恢复了list1的同时,竟然将list2中的内容也修改了?

思考和猜想

我们是将list1 给append到了list2的末尾,虽然刚接触Python,但是直觉告诉我,这种情况和深浅拷贝有关,可能是因为,append将list1浅拷贝给了list2[0],于是list1 和 list2[0] 这两个列表变量使用的是同一块内存空间,所以在我恢复list1的内容时,list2[0]的内容也恢复了。

调试中,后几轮循环也都出现了相同的情况,即恢复list1的同时,list2中对应位置也发生了变化。那么接下来就应该查一下append函数

append()函数

经查阅,Python列表中的append()函数,实际上追加的是对象的引用。

引用?那么确定就是浅拷贝问题无疑了。问题出现的原因和我们猜想的应该一致。

解决方法

显然应该通过深拷贝来将修改后的 list1 添加到 list2 的对应位置。

再经过查阅,得知Python中列表的切片是可以进行深拷贝的。
也就是说,我们对一个列表切片后得到的是一个新的列表(此处的新,是指地址不同),需要注意的是,切片时,即使是整个列表一起切(list1[:]),也会生成一个全新的列表,这个新列表的内容(值)和旧列表(list1)相同,但地址不同。
因此我们只需要在 流程 的 第4步 处,进行修改即可

修改后的代码

#原列表
list1 = [1, 2, 3, 4]

#目标列表
list2 = []

#依次遍历原列表list1的每一个元素each
for each in list1:

    #记录当次遍历到的元素所在的下标index
    index = list1.index(each)

    #在list1中删除下标为index的元素
    list1.pop(index)

    #将删除元素后的list1添加到目标列表list2的末尾
    list2.append(list1[:])  #添加的是list1的一个深拷贝,这样后续恢复list1时,就不会影响list2的内容

    #恢复list1,方便下一次循环时重新使用
    list1.insert(index,each)

print(list2)

运行结果:

[[2, 3, 4], [1, 3, 4], [1, 2, 4], [1, 2, 3]]

小结

这道题本身不是一道难题,它的思路很清晰。但是刚学一门新的语言,一段很短很简单的代码也可能牵扯出很多原本不了解的问题(append函数的细节),同时,编程语言间很多东西都是相通的(深拷贝和浅拷贝的坑)。