Removing Item From List - during iteration - what&

2019-01-01 09:36发布

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 ?

8条回答
裙下三千臣
2楼-- · 2019-01-01 10:04

It removes the first occurrence, and then checks for the next number in the sequence. Since the sequence has changed it takes the next odd number and so on...

  • take "a"
  • remove "a" -> the first item is now "b"
  • take the next item, "c" -...
查看更多
君临天下
3楼-- · 2019-01-01 10:07

OK, I'm a little late to the party here, but I've been thinking about this and after looking at Python's (CPython) implementation code, have an explanation I like. If anyone knows why it's silly or wrong, I'd appreciate hearing why.

The issue is moving through a list using an iterator, while allowing that list to change.

All the iterator is obliged to do is tell you which item in the (in this case) list comes after the current item (i.e. with the next() function).

I believe the way iterators are currently implemented, they only keep track of the index of the last element they iterated over. Looking in iterobject.c one can see what appears to be a definition of an iterator:

typedef struct {
    PyObject_HEAD
    Py_ssize_t it_index;
    PyObject *it_seq; /* Set to NULL when iterator is exhausted */
} seqiterobject;

where it_seq points to the sequence being iterated over and it_index gives the index of the last item supplied by the iterator.

When the iterator has just supplied the nth item and one deletes that item from the sequence, the correspondence between subsequent list elements and their indices changes. The former (n+1)st item becomes the nth item as far as the iterator is concerned. In other words, the iterator now thinks that what was the 'next' item in the sequence is actually the 'current' item.

So, when asked to give the next item, it will give the former (n+2)nd item(i.e. the new (n+1)st item).

As a result, for the code in question, the iterator's next() method is going to give only the n+0, n+2, n+4, ... elements from the original list. The n+1, n+3, n+5, ... items will never be exposed to the remove statement.

Although the intended activity of the code in question is clear (at least for a person), it would probably require much more introspection for an iterator to monitor changes in the sequence it iterates over and, then, to act in a 'human' fashion.

If iterators could return prior or current elements of a sequence, there might be a general work-around, but as it is, you need to iterate over a copy of the list, and be certain not to delete any items before the iterator gets to them.

查看更多
登录 后发表回答