问题描述:
- 循环按序号遍历列表时,对列表中元素删除,可能会影响启遍因效果
# 功能描述:将从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']
如果还有更好的方法,请留言讨论,谢谢