Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Removing Item From List - during iteration - what's wrong with this idiom?

Tags:

python

loops

list

As an experiment, I did this:

letters=['a','b','c','d','e','f','g','h','i','j','k','l'] for i in letters:     letters.remove(i) print letters 

The last print shows that not all items were removed ? (every other was).

IDLE 2.6.2       >>> ================================ RESTART ================================ >>>  ['b', 'd', 'f', 'h', 'j', 'l'] >>>  

What's the explanation for this ? How it could this be re-written to remove every item ?

like image 534
monojohnny Avatar asked May 24 '10 11:05

monojohnny


People also ask

Can we remove from list while iterating?

In Java 8, we can use the Collection#removeIf API to remove items from a List while iterating it.

Can we remove element from list while iterating Python?

We can delete multiple elements from a list while iterating, but we need to make sure that we are not invalidating the iterator. So either we need to create a copy of the list for iteration and then delete elements from the original list, or we can use the list comprehension or filter() function to do the same.

What will be the output if you try to remove an item which is not in the list?

If the given element is not present in the list, it will throw an error saying the element is not in the list. The remove () method does not return any value. The remove () takes the value as an argument, so the value has to pass with the correct datatype.

How do you remove the current element from a list in Python?

In Python, use list methods clear() , pop() , and remove() to remove items (elements) from a list. It is also possible to delete items using del statement by specifying a position or range with an index or slice.


2 Answers

Some answers explain why this happens and some explain what you should've done. I'll shamelessly put the pieces together.


What's the reason for this?

Because the Python language is designed to handle this use case differently. The documentation makes it clear:

It is not safe to modify the sequence being iterated over in the loop (this can only happen for mutable sequence types, such as lists). If you need to modify the list you are iterating over (for example, to duplicate selected items) you must iterate over a copy.

Emphasis mine. See the linked page for more -- the documentation is copyrighted and all rights are reserved.

You could easily understand why you got what you got, but it's basically undefined behavior that can easily change with no warning from build to build. Just don't do it.

It's like wondering why i += i++ + ++i does whatever the hell it is it that line does on your architecture on your specific build of your compiler for your language -- including but not limited to trashing your computer and making demons fly out of your nose :)


How it could this be re-written to remove every item?

  • del letters[:] (if you need to change all references to this object)
  • letters[:] = [] (if you need to change all references to this object)
  • letters = [] (if you just want to work with a new object)

Maybe you just want to remove some items based on a condition? In that case, you should iterate over a copy of the list. The easiest way to make a copy is to make a slice containing the whole list with the [:] syntax, like so:

#remove unsafe commands commands = ["ls", "cd", "rm -rf /"] for cmd in commands[:]:   if "rm " in cmd:     commands.remove(cmd) 

If your check is not particularly complicated, you can (and probably should) filter instead:

commands = [cmd for cmd in commands if not is_malicious(cmd)] 
like image 146
badp Avatar answered Sep 20 '22 16:09

badp


You cannot iterate over a list and mutate it at the same time, instead iterate over a slice:

letters=['a','b','c','d','e','f','g','h','i','j','k','l'] for i in letters[:]: # note the [:] creates a slice      letters.remove(i) print letters 

That said, for a simple operation such as this, you should simply use:

letters = [] 
like image 41
Michael Aaron Safyan Avatar answered Sep 23 '22 16:09

Michael Aaron Safyan