有一个笔试题,题目很简单就是“生成所有三位数的组合,其中每个数字范围是1到4,且三位数不能有重复”。
然后写出来这个代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| list = []
for i in range(1,5): for j in range(1,5): for z in range(1,5): list.append(f'{i},{j},{z}')
print (list)
for x in list: if x[0] == x[2] or x[0] == x[4] or x[2] == x[4]: list.remove(x) else: continue
print (list)
|
说实话,这个代码写麻烦了,这是另外一回事,但是这个代码看上去是对的,但是其实跑出来是不对的。跑之后就会发现, ‘1,2,2’,’2,1,1’这样的记录还是会保留下来。
原因就出来list.remove(x)
上,它会导致迭代器跳过元素:修改列表时,迭代器的索引会失效,导致部分元素未被检查。
举个例子,假设有一个列表[A, B, C, D],当使用for循环遍历时,第一次迭代处理A,指针指向索引0。处理完后,指针移动到索引1处理B。如果此时删除了B,列表变为[A, C, D],那么下一个元素应该是索引1的C,但指针已经移动到索引1,所以会跳过C,直接处理索引2的D,导致C未被处理。比如这个代码:
1 2 3 4 5
| my_list = [1, 2, 3, 4] for x in my_list: if x % 2 == 0: my_list.remove(x) print(my_list)
|
- 这个例子里,列表为 [1, 2, 3, 4],迭代顺序为索引 0 → 1 → 2 → 3。
- 第一次迭代:处理 1(索引 0)。
- 第二次迭代:处理 2(索引 1)。
- 如果删除 2,列表变为 [1, 3, 4]。
- 此时迭代器的“下一个索引”仍为 2,但列表的索引已缩短。原本的 3 现在位于索引 1,但迭代器会跳到索引 2,导致 3 被跳过。
- 第三次迭代:处理 4(索引 2),而 3 未被处理
所以比较好的解决方案是改成用一个新的list去append:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| list = [] new_list = []
for i in range(1,5): for j in range(1,5): for z in range(1,5): list.append(f'{i},{j},{z}')
print (list)
for x in list: print(x) if x[0] == x[2] : print("第1位和第二位有相同",x[0],x[2],"干掉!") elif x[0] == x[4]: print("第1位和第三位有相同",x[0],x[4],"干掉!") elif x[2] == x[4]: print("第二位和第三位有相同",x[2],x[4],"干掉!") else: new_list.append(x)
print (new_list)
|
或者从后往前删除元素,避免索引前移。
1 2 3 4 5
| my_list = [1, 2, 3, 4] for i in range(len(my_list)-1, -1, -1): if my_list[i] % 2 == 0: my_list.pop(i) print(my_list)
|
写此文章,记录一下,避免以后踩坑。