迭代时从列表中删除项目而不使用Python中的额外内存

时间:2010-04-13 11:44:12

标签: python list iteration

我的问题很简单:我有一长串的元素我想迭代并根据条件检查每个元素。根据条件的结果,我想删除列表的当前元素,并继续像往常一样迭代它。

我已经在这个问题上阅读了其他几个主题。提出两种解决方案。要么从列表中创建一个字典(这意味着要复制已经填满我所有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

8 个答案:

答案 0 :(得分:13)

li = [ x for x in li if condition(x)]

以及

li = filter(condition,li) 

Thanks to Dave Kirby

答案 1 :(得分:6)

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

创建列表的过滤副本更有效,可以使用Marcelo显示的列表推导,也可以使用filter或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

答案 2 :(得分: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足够智能,可以调整列表大小,而无需为列表的新副本分配内存。

答案 3 :(得分:4)

内置过滤器功能只是为了这样做:

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

答案 4 :(得分:1)

这个怎么样?

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

它返回新列表,但是来自subtle_condition

答案 5 :(得分: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]

答案 6 :(得分: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

答案 7 :(得分: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()将其反转。这也不会重复您的列表。