如何在循环内正确修改Python中的迭代器
我基本上需要做的是检查一个列表中的每个元素,如果符合某些条件,我想把它从列表中删除。
比如说,假设有这样一个情况:
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