如何在遍历列表时删除元素而不复制列表

2 投票
7 回答
3393 浏览
提问于 2025-04-15 19:05

我在这个Python的for循环上浪费了一点时间:

class MyListContainer:
    def __init__(self):
        self.list = []

    def purge(self):
        for object in self.list:
            if (object.my_cond()):
                self.list.remove(object)
        return self.list

container = MyListContainer()

# now suppose both obj.my_cond() return True
obj1 = MyCustomObject(par)
obj2 = MyCustomObject(other_par)

container.list = [obj1, obj2]

# returning not an empty list but [obj2]
container.purge()

它没有按照我预期的那样工作,因为当“purge”循环删除列表中的第一个对象时,第二个对象就会被移动到列表的开头,然后循环就结束了。

我通过在for循环之前复制self.list来解决了这个问题:

...
local_list = self.list[:]
for object in local_list:
...

我想for循环停止工作的原因是因为我在改变原始列表的长度。有人能解释一下这个问题吗?

有没有更“优雅”的方法来解决这个问题?如果列表里面的元素超过几个,每次复制它似乎不是个好主意。

也许filter()函数是合适的,但我希望能有其他的解决方法。

我还是个新手。


总结一下你们有用的回答:

  • 不要编辑你正在循环的列表
  • 复制列表或者使用列表推导式
  • 复制列表不会浪费你的内存,或者在这种情况下,谁会在意呢

7 个回答

3

只需创建一个新的列表:

def purge(self):
    self.list = [object for object in self.list if not object.my_cond()]
    return self.list

在你分析过程序性能,确认这个方法确实是你应用程序的瓶颈之前,先别急着进行任何优化。(我敢打赌,它不会是瓶颈。)

5

别试了。真的别试。直接复制一份或者生成一个新的列表就行。

2

使用过滤器(或者叫列表推导)是最好的选择。如果你想在原地处理,可以用下面这样的方式:

purge = []
for i,object in enumerate(self.list):
    if object.mycond()
        purge.append(i)
for i in reversed(purge):
    del self.list[i]

另外,你也可以用列表推导来创建一个清理列表,简化版看起来像这样:

for i in reversed([ i for (i,o) in enumerate(self.list) if o.mycond() ]):
    del self.list[i]

撰写回答