如何在遍历列表时删除元素而不复制列表
我在这个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]