随笔:列表(序列)遍历的一个小坑

问题描述:

  • 循环按序号遍历列表时,对列表中元素删除,可能会影响启遍因效果
# 功能描述:将从l1中,删除s1中的元素
l1 = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"]
s1 = {"a", "c", "G", "i", "k"}
for item in l1:
    if item in s1:
        l1.remove(item)
print("l1=", l1)
# l1= ['b', 'd', 'e', 'f', 'g', 'h', 'j']
  • 从输出结果看,达到想要的效果
  • 但如果 s1中元素在l1中有两个连续元素时,结果就出现的问题,如下
l1 = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"]
s1 = {"a", "b", "g", "i", "j"}     # a、b  和 i、j 在l1 中是连续的
for item in l1:
    if item in s1:
        l1.remove(item)
print("l1=", l1)
# l1= ['b', 'c', 'd', 'e', 'f', 'h', 'j', 'k']
  • 这不是我们想要的结果,其中b 、j 都未被删除

分析原因

  • 为找到原因,我们从以下几方面分析
  • 1、l1的元素是否都依次遍历到了
    • a.遍历的次数是否够
    • b.遍历的元素是否全覆盖
  • 2、for循环是否会因为待遍历列表发生改变,而改变执行顺序
  • 3、for循环次数是否在第一次执行时就已明确次数
  • 为此我们增加调试信息 count、i 、temp_list
l1 = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"]
s1 = {"a", "b", "g", "i", "j"}     # a、b  和 i、j 在l1 中是连续的
count = 0   							# 循环外计数器
temp_list=[]							# 记录遍历到的元素
print(count," "," ",l1)					# 打印起始状态的信息
for i,item in enumerate(l1):			# 循环遍历,并计数
    count += 1							
    temp_list.append(item)
    if item in s1:
        l1.remove(item)					# 从列表中删除s1中存在的元素
    print(count, i,item,l1)				# 打印遍历一个元素的的信息
print("temp_list=",temp_list)
print("l1=", l1)
# 0     ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# 1 0 a ['b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# 2 1 c ['b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# 3 2 d ['b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# 4 3 e ['b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# 5 4 f ['b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# 6 5 g ['b', 'c', 'd', 'e', 'f', 'h', 'i', 'j', 'k']
# 7 6 i ['b', 'c', 'd', 'e', 'f', 'h', 'j', 'k']
# 8 7 k ['b', 'c', 'd', 'e', 'f', 'h', 'j', 'k']
# temp_list= ['a', 'c', 'd', 'e', 'f', 'g', 'i', 'k']
# l1= ['b', 'c', 'd', 'e', 'f', 'h', 'j', 'k']
  • 通过上面调试发现:
  • 设想结果: [“c”, “d”, “e”, “f”, “h”, “k”],但执行结果中多了"b"、“j”
  • 遍历次数未达到我们预想次数,应该遍历11次,实际遍历8次
  • for循环计数(enumerate() ),未因删除而跳号(0、1、2 … 7)
  • for循环每次都针对最新 待遍历对象按次数取出一个元素,如第一次执行完后,l1=[‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘i’, ‘j’, ‘k’],第二次执行时,i=1(从0开始),取l1[i]=l1[1]=‘c’,此时就跳过’b’元素,原始列表第二个元素’b’变成了此时列表的第一个元素,for 循环没有因删除一个元素而次数减一次或加一次,还按部就班的执行第二次。
  • 同样执行完第五次时,l1=[‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘i’, ‘j’, ‘k’],执行第六次时,i=5,l1[5]=‘g’,需要会被删除,执行完后,l1=[‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘h’, ‘i’, ‘j’, ‘k’],此时l1[5]=“h”,l1[6]=“i”,故执行第七次时,i=6,“h”元素也被跳过,"i"没在s1中被保留

总结

  • 由此可见,for循环遍历筛选元素时,循环会按次数顺序,从最新列表中取出顺序号对应的元素,为防止漏项或重复判断,请不要对 in 后面的对象进行增删操作。
  • for循环遍历时,并不是根据t第一次执行时可变对象长度来控制循环次数,中途可变对象长度发生改变,只要未到最后一个元素,循环不会结束,故for循环次数,与待循环遍历的对象长度无必须联系。

解决方法一:将列表复制一份,遍历备份列表,操作原始列表

l1 = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"]
s1 = {"a", "b", "g", "i", "j"}  # a、b  和 i、j 在l1 中是连续的
l2 = l1.copy()								# 复制l1
count = 0
temp_list = []
print(count, " ", " ", l1)
for i, item in enumerate(l2):				# 对l2遍历
    count += 1
    temp_list.append(item)
    if item in s1:
        l1.remove(item)
    print(count, i, item, l2)

print("temp_list=", temp_list)
print("l1=", l1)
# 0     ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# 1 0 a ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# 2 1 b ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# 3 2 c ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# 4 3 d ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# 5 4 e ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# 6 5 f ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# 7 6 g ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# 8 7 h ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# 9 8 i ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# 10 9 j ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# 11 10 k ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# temp_list= ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# l1= ['c', 'd', 'e', 'f', 'h', 'k']

如果还有更好的方法,请留言讨论,谢谢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值