Python3遍历嵌套列表删除spam时遗漏最后一个元素问题求助
这不是Python的bug,是遍历列表的同时删除元素导致的经典索引偏移问题。
问题原因
Python的for循环遍历列表时,内部会维护一个自增的索引指针,每次迭代指针向后移动一位。如果遍历过程中删除了当前位置前面的元素,列表整体长度会缩短,后续元素会整体向前移位,就会导致指针跳过下一个待检查的元素。
以你遇到的最后一个残留spam的子列表为例,原始值为["spam", "egg", "spam", "spam", "bacon", "spam"],遍历删除过程如下:
- 初始指针指向索引0,元素为spam,执行删除后列表变为
["egg", "spam", "spam", "bacon", "spam"],指针自增到1 - 指针指向索引1,元素为spam,执行删除后列表变为
["egg", "spam", "bacon", "spam"],指针自增到2 - 指针指向索引2,元素为bacon,无操作,指针自增到3
- 指针指向索引3,元素为spam,执行删除后列表变为
["egg", "spam", "bacon"],此时列表长度为3,下一轮指针要自增到4,超出列表长度,循环终止,索引1位置残留的spam没有被检查到,就是你看到的残留问题。
解决方案
方案1:列表推导式生成新列表(最简洁,无副作用)
直接通过列表推导式过滤掉所有spam,不需要修改原列表,不会出现索引问题:
menu = [ ["egg", "bacon"], ["egg", "sausage", "bacon"], ["egg", "spam"], ["egg", "bacon", "spam"], ["egg", "bacon", "sausage", "spam"], ["spam", "bacon", "sausage", "spam"], ["spam", "sausage", "spam", "bacon", "spam", "tomato", "spam"], ["spam", "egg", "spam", "spam", "bacon", "spam"], ] clean_menu = [[item for item in meal if item != "spam"] for meal in menu] for meal in clean_menu: print(meal)
方案2:倒序遍历删除(需修改原列表时使用)
倒序遍历列表的索引,删除元素时只会影响已经遍历过的后面的索引,不会导致前面待检查的元素偏移:
for meal in menu: # 从最后一个索引倒着遍历到0 for idx in range(len(meal)-1, -1, -1): if meal[idx] == "spam": del meal[idx] print(meal)
方案3:按spam个数重复删除
统计当前子列表中spam的数量,重复执行对应次数的remove操作:
for meal in menu: for _ in range(meal.count("spam")): meal.remove("spam") print(meal)
内容的提问来源于stack exchange,提问作者4044Amar
相关产品推荐
相关产品推荐

