现在的位置: 首页 > 综合 > 正文

Delete elements while iterating a list

2013年10月01日 ⁄ 综合 ⁄ 共 1581字 ⁄ 字号 评论关闭
文章目录

当我们想要在遍历一个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

抱歉!评论已关闭.