14

I am new to Python and experimenting with lists I am using Python 3.2.3 (default, Oct 19 2012, 20:13:42), [GCC 4.6.3] on linux2

Here is my samplecode

>>> l=[1,2,3,4,5,6]
>>> for i in l:
...     l.pop(0)
...     print(l)
... 

I would expect the following output

1
[2, 3, 4, 5, 6]
2
[3, 4, 5, 6]
3
[4, 5, 6]
4
[5, 6]
5
[6]
6
[]

Instead I am getting this

1
[2, 3, 4, 5, 6]
2
[3, 4, 5, 6]
3
[4, 5, 6]

The for-loop stops iterating after 3 turns. Can somebody explain why?

Zoe
  • 27,060
  • 21
  • 118
  • 148
user1913757
  • 151
  • 1
  • 1
  • 5
  • @NedBatchelder: While the root cause is the same, the question is substantially different. The reason for calling `pop` isn't even the same (the linked is attempting to skip, this one is not). – Guvante Dec 19 '12 at 00:17

6 Answers6

16

Unrolling a bit (the caret (^) is at the loop "index"):

your_list = [1,2,3,4,5,6]
             ^

after popping off the first item:

your_list = [2,3,4,5,6]
             ^

now continue the loop:

your_list = [2,3,4,5,6]
               ^

Now pop off the first item:

your_list = [3,4,5,6]
               ^

Now continue the loop:

your_list = [3,4,5,6]
                 ^

Now pop off first item:

your_list = [4,5,6]
                 ^

Now continue the loop -- Wait, we're done. :-)


>>> l = [1,2,3,4,5,6]
>>> for x in l:
...     l.pop(0)
... 
1
2
3
>>> print l
[4, 5, 6]
kindall
  • 178,883
  • 35
  • 278
  • 309
mgilson
  • 300,191
  • 65
  • 633
  • 696
  • How does 2nd iteration's print show popped value 2? Since at end of 1st iteration the pointer is at 2, then the next iteration pointer should be at 3. So should it not have printed 3 instead of 2? – variable Nov 13 '19 at 17:59
  • No, because I'm always popping the `0`th element rather than the element at the index of the current list-iterator pointer. – mgilson Nov 15 '19 at 05:27
13

You could use a while loop rather than a for loop for this task.

while len(some_list)>0 :
    some_list.pop(0)

A for loop will actually iterate over each item in the list, which will not work as the indices in the list will change with each deletion, and you will not end up getting all items.

However, a while loop will check a condition every time the loop is run, and if it is still true, run the code again. Here we specify that the length of the list has to be more than 0, i.e. there has to be content in the list.

Toastrackenigma
  • 7,604
  • 4
  • 45
  • 55
johnny brin
  • 131
  • 1
  • 2
  • 3
    You should have to explain a downvote. This answer is correct. – Johnny Jun 30 '18 at 17:21
  • This is a good answer to handle the problem faced by the asker, but it is not the computer science answer to the original question. :P I did use it to solve for my issue though so ty. – Rob Mar 23 '19 at 01:06
3

You have to be careful when attempting to modify collections you are iterating over. In this case, the list keeps track of the "current position" with a simple integer index. When you use pop(), everything changes index, and so elements are skipped.

On the first iteration of the loop, i is l[0]. Then you pop the list, then you access l[1], which is what originally was at l[2]. Then you pop the list, and the next iteration accesses l[2], which is what used to be at l[4], etc.

There's no need to pop elements in this code anyway, presumably you are doing something more complex in your real code.

Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
2

Python doesn't support altering the length of a list while you iterate over it. Work on a copy or use a list comprehension instead.

Think about how Python is actually doing the for loop - it counts up through the elements, returning the item at the current index. When you remove one, the index means a different element.

Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
  • In one case it may work. When popping last element on each iteration. – Rohit Jain Dec 18 '12 at 18:42
  • Potentially there are situations it could work. It's not something you should rely on however, there are better ways. – Gareth Latty Dec 18 '12 at 18:43
  • 2
    Python does support modifying a list while iterating over it, and it works in a consistent and predictable manner once you understand what's happening. BTW, another useful workaround for some use cases is to operate on the list in reverse order. – kindall Dec 18 '12 at 19:14
  • 2
    There's a big difference between the **can** do something and **should** do something ;-). You definitely *can* modify a list as you iterate over it, but I would argue that most of the time, you probably *shouldn't*. – mgilson Dec 18 '12 at 19:17
2

Code

l = [1,2,3,4,5,6]
for i in range(len(l)):
    l.pop(0)
    print(l)

returns

[2, 3, 4, 5, 6]
[3, 4, 5, 6]
[4, 5, 6]
[5, 6]
[6]
[]
xcen
  • 652
  • 2
  • 6
  • 22
1
li = [1, 2, 3, 4, 5]

while li:
    # For descending order
    # print(li.pop())

    # For ascending order
    print(li.pop(0))