Python遍历列表删除0频字母时隔项漏删的问题排查
列表遍历删除元素不彻底问题排查
问题相关代码
这段代码预期实现输入句子的字符频次统计功能,最终返回(字符, 出现次数)格式的元组列表,自动剔除出现次数为0的字符:
import string CHARACTERS = list(string.ascii_letters) + [" "] def letter_frequency(sentence): frequencies = [(c, 0) for c in CHARACTERS] for letter in sentence: index = CHARACTERS.index(letter) frequencies[index] = (letter, frequencies[index][1] + 1) for item in frequencies: if item[1] == 0: frequencies.remove(item) return frequencies
实际运行时会出现计数为0的项无法被完全删除、每隔一个漏删一个的问题。
出错根本原因
问题出在遍历列表的同时直接修改原列表结构,这是Python列表操作的常见误区:
Python的for循环遍历列表时,是按照从0开始递增的索引位置逐个取值的,遍历过程不会因为列表内容变动自动调整索引计数:
- 当循环遍历到某个计数为0的元素,调用
remove()方法删除该元素后,列表中该元素后方的所有元素会自动向前移动一位,列表总长度减1 - 此时循环的索引指针不会回退,下一轮循环会直接取当前指针+1位置的元素,刚好跳过被删元素后方紧挨着的那个元素,最终就表现为隔一个删一个、删不干净的现象。
举个极简复现示例:假设待遍历的列表为[('a',0), ('b',0), ('c',0), ('d',1)]:
- 第一轮循环索引为0,取到
('a',0),符合删除条件,删除后列表变为[('b',0), ('c',0), ('d',1)] - 第二轮循环索引自动加1为1,此时直接取索引1位置的元素,拿到的是
('c',0),原本排在第二位的('b',0)因为列表前移到了索引0位置,直接被遍历逻辑跳过,不会被删除。
修复方案
两种可直接运行的改法,更推荐第二种,执行效率更高:
- 方案1:遍历原列表的浅副本做判断,再修改原列表
# 遍历原列表的副本,遍历过程不会受原列表改动影响 for item in frequencies.copy(): if item[1] == 0: frequencies.remove(item)
- 方案2:使用列表推导式直接过滤生成新列表,替换原有删除循环即可
# 只保留计数大于0的项,逻辑简洁且时间复杂度更低 frequencies = [item for item in frequencies if item[1] > 0]
内容的提问来源于stack exchange,提问作者Joseph Coleridge
相关产品推荐
相关产品推荐

