2010-04-13 35 views
9

我的问题很简单:我有一长串元素,我想要遍历并检查每个元素是否符合条件。根据条件的结果,我想删除列表中的当前元素,并像往常一样继续迭代它。在迭代时从列表中删除项目,而无需在Python中使用额外的内存

我已阅读了有关此问题的其他一些线索。提出两种解决方案。要么从列表中创建一个字典(这意味着制作所有数据的副本,这些数据已经填满了我的案例中的所有RAM)。要么反过来列表(这打破了我想实现的算法的概念)。

有没有比这更好或更优雅的方式来做到这一点?

def walk_list(list_of_g): 
    g_index = 0 
    while g_index < len(list_of_g): 
     g_current = list_of_g[g_index] 
     if subtle_condition(g_current): 
      list_of_g.pop(g_index) 
     else: 
      g_index = g_index + 1 
+2

所有这些的副本:http://stackoverflow.com/search?q=%5Bpython%5D+list+remove。具体http://stackoverflow.com/questions/1207406/remove-items-from-a-list-while-iterating-in-python – 2010-04-13 11:48:38

+2

是的我在发布我的问题之前仔细阅读你链接到的其他线程。在另一个线索中提出的答案没有一个解决我关心的问题。这是因为我的问题有所不同,即复制列表被排除在外并且向后遍历。 – xApple 2010-04-13 13:41:09

回答

6

这里是如果你非得从原来的列表中删除项目的备选答案,你没有足够的内存来进行复印 - 移动该项目在列表中向下自己:

def walk_list(list_of_g): 
    to_idx = 0 
    for g_current in list_of_g: 
     if not subtle_condition(g_current): 
      list_of_g[to_idx] = g_current 
      to_idx += 1 
    del list_of_g[to_idx:] 

这将移动每一个项目(实际上是一个指针,以每个项目)正好一次,所以会为O(N)。函数结尾处的del语句将删除列表末尾的所有不需要的项目,我认为Python足够智能,可以调整列表的大小,而无需为列表的新副本分配内存。

+0

我喜欢它。即使它不是“Pythonic”,它回答了这个问题,它仍然非常漂亮。 – zildjohn01 2011-07-12 00:36:16

1

这个怎么样?

[x for x in list_of_g if not subtle_condition(x)] 

其回报与subtle_condition

1

例外,为了简单起见新的列表,使用列表理解:

def walk_list(list_of_g): 
    return [g for g in list_of_g if not subtle_condition(g)] 

当然,这不会改变原来的列表,因此调用代码将不得不不同。

如果你真的想变异列表(很少的最佳选择),倒着走路更简单:

def walk_list(list_of_g): 
    for i in xrange(len(list_of_g), -1, -1): 
     if subtle_condition(list_of_g[i]): 
      del list_of_g[i] 
13
li = [ x for x in li if condition(x)] 

li = filter(condition,li) 

Thanks to Dave Kirby

+2

正如Alex Martelli在http://stackoverflow.com/a/1208792/914874中建议的那样:li [:] = [x for li如果条件(x)]是更好的方法。 – Eduardo 2013-07-05 11:56:57

+0

@Eduardo有趣!非常感谢! – 2013-07-05 19:45:52

4

内置-in过滤功能只是为了做到这一点:

list_of_g = filter(lambda x: not subtle_condition(x), list_of_g) 
6

从列表中删除项目很昂贵,因为python必须将g_index上面的所有项目复制到一个地方。如果要删除的项目数量与列表N的长度成比例,则算法将为O(N ** 2)。如果列表足够长以填满RAM,那么您将等待很长时间才能完成。

更有效的创建列表的筛选副本,或者使用列表理解为马塞洛表现,或者使用过滤器或itertools.ifilter功能:

g_list = filter(not_subtle_condition, g_list) 

如果您不需要使用新的列表,并只希望一次迭代它,那么它最好使用IFilter的,因为这将不会创建第二个列表:

for g_current in itertools.ifilter(not_subtle_condtion, g_list): 
    # do stuff with g_current 
1

听起来像一个很好的用例的过滤功能。

def should_be_removed(element): 
    return element > 5 

a = range(10) 
a = filter(should_be_removed, a) 

但是,这将不会在迭代时删除列表(我也不推荐它)。如果内存空间(或其他性能方面的原因),你真的需要它,你可以做到以下几点:

i = 0 
while i < len(a): 
    if should_be_removed(a[i]): 
     a.remove(a[i]) 
    else: 
     i+=1 
    print a 
+0

按元素删除可能会更慢或更快,具体取决于列表的内容。 – Alcides 2010-04-13 11:56:13

0

如果执行反向迭代,你可以删除的飞行元素,而不会影响你下次访问指数:

numbers = range(20) 

# remove all numbers that are multiples of 3 
l = len(numbers) 
for i, n in enumerate(reversed(numbers)): 
    if n % 3 == 0: 
     del numbers[l - i - 1] 

print numbers 

enumerate(reversed(numbers))只是风格上的选择。如果你需要旅行,以便列表

l = len(numbers) 
for i in range(l-1, -1, -1): 
    n = numbers[i] 
    if n % 3 == 0: 
     del numbers[i] 

,可以前后颠倒迭代后.reverse()扭转它在的地方:你可以使用一个范围,如果这是更清晰给你。这不会重复您的列表。

相关问题