python不要轻易的去remove元素

有一个笔试题,题目很简单就是“生成所有三位数的组合,其中每个数字范围是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, 3, 4](2 被删除,但 4 未被处理)
  1. 这个例子里,列表为 [1, 2, 3, 4],迭代顺序为索引 0 → 1 → 2 → 3。
  2. 第一次迭代:处理 1(索引 0)。
  3. 第二次迭代:处理 2(索引 1)。
  4. 如果删除 2,列表变为 [1, 3, 4]。
  5. 此时迭代器的“下一个索引”仍为 2,但列表的索引已缩短。原本的 3 现在位于索引 1,但迭代器会跳到索引 2,导致 3 被跳过。
  6. 第三次迭代:处理 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):
#if i !=j and j != z and i !=z:
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) # 输出: [1, 3]

写此文章,记录一下,避免以后踩坑。

感谢您请我喝咖啡~O(∩_∩)O,如果要联系请直接发我邮箱chenx1242@163.com,我会回复你的
-------------本文结束感谢您的阅读-------------