Python通过列表匹配实现列表去重的代码异常原因解析
列表去重代码异常原因分析
问题描述
采用并行列表去重方案:维护新列表t存储已处理元素,遍历原列表时检查目标元素(代码中存在逻辑误用)是否在t中,不在则加入t,在则从原列表删除。代码如下:
# nums = [1,1,2] or [0,0,1,1,1,2,2,3,3,4] t = [] nums_new = nums for i in nums: if nums[i] not in t: t.append(nums[i]) else: nums_new.remove(nums[i]) nums = nums_new print(nums)
测试nums = [1,1,2]时返回[1,2]看似正常,但测试nums = [0,0,1,1,1,2,2,3,3,4]时输出异常为[0, 1, 2, 2, 3, 3, 4]。
核心错误点
1. 引用赋值而非列表拷贝
nums_new = nums并没有创建新列表,只是让nums_new和nums指向内存中同一个列表。修改nums_new的同时,nums也会同步变化,本质上操作的是同一个列表。
2. 混淆遍历元素与索引
for i in nums中的i是列表的元素值,不是索引,但代码中错误地用nums[i]去取元素。第一个测试用例刚好“歪打正着”:nums=[1,1,2]的元素1对应索引1的元素是1,元素2对应索引2的元素是2,逻辑上刚好匹配;但第二个测试用例中,这种错误逻辑会导致判断的元素并非当前遍历到的i,而是索引为i的元素,完全偏离了去重的初衷。
3. 遍历列表时直接修改列表
在for循环中删除列表元素,会导致列表长度缩短、后续元素前移,而Python的for循环迭代器是在循环开始时生成的,会按初始列表的元素顺序遍历,从而跳过某些前移后的元素,导致重复项未被处理。
具体执行步骤(以nums = [0,0,1,1,1,2,2,3,3,4]为例)
初始状态:nums和nums_new指向同一列表[0,0,1,1,1,2,2,3,3,4],t = []
- 第一次循环,
i=0(遍历到第一个元素0):- 取
nums[i] = nums[0] = 0,0不在t中,t变为[0]
- 取
- 第二次循环,
i=0(遍历到第二个元素0):- 取
nums[i] = nums[0] = 0,0在t中,执行nums_new.remove(0),列表变为[0,1,1,1,2,2,3,3,4](删除第一个出现的0)
- 取
- 第三次循环,
i=1(遍历到第三个元素1):- 取
nums[i] = nums[1] = 1,1不在t中,t变为[0,1]
- 取
- 第四次循环,
i=1(遍历到第四个元素1):- 取
nums[i] = nums[1] = 1,1在t中,执行nums_new.remove(1),列表变为[0,1,1,2,2,3,3,4](删除第一个出现的1)
- 取
- 第五次循环,
i=1(遍历到第五个元素1):- 取
nums[i] = nums[1] = 1,1在t中,执行nums_new.remove(1),列表变为[0,1,2,2,3,3,4](删除第一个出现的1)
- 取
- 第六次循环,
i=2(遍历到第六个元素2):- 取
nums[i] = nums[2] = 2,2不在t中,t变为[0,1,2]
- 取
- 第七次循环,
i=2(遍历到第七个元素2):- 此时列表是
[0,1,2,2,3,3,4],取nums[i] = nums[2] = 2,2在t中,执行nums_new.remove(2)时,列表元素前移,迭代器按初始顺序遍历的特性导致后续的2被跳过判断,最终这个2未被删除
- 此时列表是
- 后续循环中,3和4的重复项也因为类似的遍历-修改冲突,出现未被完全删除的情况,最终输出
[0, 1, 2, 2, 3, 3, 4]
内容的提问来源于stack exchange,提问作者41 72 6c
相关产品推荐
相关产品推荐

