当我们想要在遍历一个list的同时去删除元素的时候,可能会遇到一些问题。先看一下下面的代码
my_list = [1, 2, 4, 5] for elem in my_list: if elem % 2 == 0: my_list.remove(elem) print my_list
我们肯定会希望这段代码的输出是[1, 5 ],但事实上,是[1, 4, 5]。猜测可能的原因是遍历my_list时,for循环创建的iter的内部储存了一个index,当遍历到2时,index = 1,而当删除2后,下一个index = 2,而此时my_list只剩下3个元素[1, 4, 5],此时的my_list[2] = 5,所以5 % 2 != 0。跳过了4这个元素。我们可以用如下的方法来解决这个问题。
从后向前遍历并删除元素
my_list = [1, 2, 4, 5] for i in range(len(my_list) - 1, -1, -1): if my_list[i] % 2 == 0: my_list.remove(my_list[i]) print my_list
这里我们从后向前遍历,并且使用下标来遍历整个list。
使用列表解析(list comprehension)
my_list = [1, 2, 4, 5] print [elem for elem in my_list if elem % 2 != 0]
使用列表解析的代码异常简单,这里有个区别就是我们创建了一个新的列表,不过这通常也是python里面常用的方式。
使用filter
print filter(lambda x: x % 2, [1, 2, 4, 5])
使用filter与用列表解析类似,同样是创建一个新列表,也是一行就完成了任务。
使用itertools模块
import itertools print [elem for elem in itertools.ifilter(lambda x: x%2, [1, 2, 4, 5])] print [elem for elem in itertools.ifilterfalse(lambda x: x%2 == 0, [1, 2, 4, 5])]
ifilter*返回一个generator用于迭代整个list
Manual Loop and remove
my_list = [1, 2, 4, 5] # in place remove dest = 0 for i in range(len(my_list)): if my_list[i] % 2 != 0: if my_list[i] != my_list[dest]: my_list[dest] = my_list[i] dest += 1 # all elements in [0, dest) is odd # so we remove all the elements in [dest, len(my_list)) del my_list[dest:len(my_list)] print my_list
性能
不同的方法效率不同,我们简单地进行一下测试,对一个随机生成的具有10000个元素的列表删除其中的偶数,迭代100次测试其不同方法的性能。
测试使用的是CPython 2.6.4,Windows 7,CPU Phenom II 945,memory 4G。
Method | Time(s) |
从后向前遍历并删除元素 | 6.537 |
使用列表解析(list comprehension) | 0.205 |
使用filter | 0.203 |
使用itertools模块 | 0.210 |
Manual Loop and remove | 0.227 |
我们可以看到,使用列表解析和filter,itertools方法的性能远超过从后向前遍历的方法。并且Manual Loop and remove的效率也没想象中的高。可以看到使用已有的功能可以轻松获得非常高的效率。
reference
http://stackoverflow.com/questions/1207406/remove-items-from-a-list-while-iterating-in-python