如何在循环内正确修改Python中的迭代器

11 投票
4 回答
12114 浏览
提问于 2025-04-17 08:32

我基本上需要做的是检查一个列表中的每个元素,如果符合某些条件,我想把它从列表中删除。

比如说,假设有这样一个情况:

list=['a','b','c','d','e']

我基本上想写(只是原则上的想法,不是我实际尝试的代码)

如果列表中的某个元素是'b'或'c',就把它从列表中删除,然后检查下一个元素。

但是

for s in list:
    if s=='b' or s=='c':
        list.remove(s)

这样做会出问题,因为当'b'被删除后,循环会直接跳到'd',而不是继续检查'c'。那么,有没有比先把元素存到一个单独的列表里再删除它们更快的方法呢?

谢谢。

4 个回答

3

这正是 itertools.ifilter 的用途所在。

from itertools import ifilter

ifilter(lambda x: x not in ['b', 'c'], ['a', 'b', 'c', 'd', 'e'])

这会给你返回一个生成器,用来处理你的列表。如果你真的需要一个列表,可以使用一些常见的方法把生成器转换成列表:

list(ifilter(lambda x: x not in ['b', 'c'], ['a', 'b', 'c', 'd', 'e']))

或者

[x for x in ifilter(lambda x: x not in ['b', 'c'], ['a', 'b', 'c', 'd', 'e'])]
9

不要重复造轮子,已经有的东西就别再自己做了。在这种情况下,可以使用filter函数和lambda表达式。这样写更符合Python的风格,而且看起来更简洁。

filter(lambda x:x not in ['b','c'],['a','b','c','d','e'])

另外,你也可以使用列表推导式。

[x for x in ['a','b','c','d','e'] if x not in ['b','c']]
13

更简单的方法是使用列表的一个副本——你可以通过切片来实现,从“开始”到“结束”这样做,像这样:

for s in list[:]:
    if s=='b' or s=='c':
        list.remove(s)

你可能考虑过这个方法,这个方法足够简单,可以直接放在你的代码里,除非这个列表真的很大,并且在代码的关键部分(比如在一个动作游戏的主循环中)。在这种情况下,我有时会使用以下的写法:

to_remove = []
for index, s in enumerate(list):
    if s == "b" or s == "c":
         to_remove.append(index)

for index in reversed(to_remove):
    del list[index]

当然,你也可以使用while循环来代替:

index = 0
while index < len(list):
   if s == "b" or s == "c":
       del list[index]
       continue
   index += 1

撰写回答