How to delete list elements while cycling the list itself without duplicate it

给你一囗甜甜゛ 提交于 2019-12-23 03:43:15

问题


I lost a little bit of time in this Python for statement:

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()

It doesn't work as I expected because when the cycle in "purge" delete the first object in list the second one is shifted to the beginning of the list and the cycle is ended.

I solved duplicating self.list before the for cycle:

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

I suppose that the for statement stop working because I'm changing the length of the original list. Can someone clarify this point ?

And is there a more "elegant" way to solve this problem ? If I have more than few elements inside the list, duplicating it every time does not seem a good idea.

Maybe the filter() function is the right one but i whish to have some other approach if any.

I'm a newbie.


To summarize your useful answers:

  • Never edit a list you are looping
  • Duplicate the list or use list comprehensions
  • Duplicating a list could not waste your memory or in this case who's mind about it

回答1:


Filter (or list comprehension) IS the way to go. If you want to do it inplace, something like this would work:

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

Or alternatively, the purge list can be made with a comprehension, a shortcut version looks like:

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



回答2:


Don't try. Just don't. Make a copy or generate a new list.




回答3:


Just make yourself a new list:

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

Reserve any optimization until you've profiled and found that this method really is the bottleneck of your application. (I bet it won't be.)




回答4:


In python variables are actually labels to data. Duplicating a list is, for the most part, making a new set of pointers to the data from the first list. Don't feel too bad about it.

List comprehensions are your friend.

e.g.

>>> a = range(20)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> [ x for x in a if x % 2 == 0 ]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]



回答5:


Your second solution in which you duplicate the list is the right way to go. Afterward you can just replace the old list with the duplicate if need be.




回答6:


It's perfectly safe to shorten the list in place if you do it in reverse!

>>> a=range(20)
>>> for i in reversed(range(len(a))):
...     if a[i]%2: del a[i]
... 
>>> a
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Another way is to reassign the whole slice

>>> a=range(20)
>>> a[:]=(x for x in a if not x%2)
>>> a
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

If the items in the list are unique, this works too

>>> a=range(20)
>>> for item in reversed(a):
...  if item%2: a.remove(item)
... 
>>> a
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Here is some more explanation in response to yuri's comment

Suppose we have

>>> a=[0,1,2,3,4,5]

Now trying naively to delete the 3rd and 4th items

>>> del a[3]
>>> del a[4]
>>> a
[0, 1, 2, 4] # didn't work because the position of all the item with index >=3 was changed

However if we do the del's in the opposite order

>>> a=[0,1,2,3,4,5]
>>> del a[4]
>>> del a[3]
>>> a
[0, 1, 2, 5] # this is the desired result

Now extend that idea over a for loop with a removal condition, and you see that removal from the live list is possible




回答7:


indeces = []
minus = 0

for i in range(self.list):
    if cond(self.list[i]):
        indeces.append(i)

for i in indeces:
    self.list = self.list[:(i-minus)].extend(self.list[i-minus+1:])


来源:https://stackoverflow.com/questions/2233388/how-to-delete-list-elements-while-cycling-the-list-itself-without-duplicate-it

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!