By Xolve

2009-10-28 14:56:31 8 Comments

l  = range(100)                         
for i in l:                         
    print i,                         
    print l.pop(0),                  
    print l.pop(0)

The above python code gives the output quite different from expected. I want to loop over items so that I can skip an item while looping.

Please explain.


@Ewan Todd 2009-10-28 15:03:34

I've been bitten before by (someone else's) "clever" code that tries to modify a list while iterating over it. I resolved that I would never do it under any circumstance.

You can use the slice operator mylist[::3] to skip across to every third item in your list.

mylist = [i for i in range(100)]
for i in mylist[::3]:

Other points about my example relate to new syntax in python 3.0.

  • I use a list comprehension to define mylist because it works in Python 3.0 (see below)
  • print is a function in python 3.0

Python 3.0 range() now behaves like xrange() used to behave, except it works with values of arbitrary size. The latter no longer exists.

@Lenna 2013-01-22 21:12:21

If a list object is required, list(range(100)) is both faster and more straightforward than this futile list comprehension. Furthermore, for i in range(100)[::3]: works.

@Tom Leys 2013-12-17 21:52:02

Also, if you just want to iterate over integers, better still to use range(0, 100, 3) or even xrange(0, 100, 3) (the latter doesn't make a full list in ram).

@wsysuper 2015-04-28 01:43:50

@Lenna,How about [range(100)] ?

@Nir Alfasi 2015-05-09 21:46:01

@wsysuper try to print your suggestion and you'll see why it's no good :)

@phi1010 2017-04-16 03:49:02

Try [*range(100)].

@Alex Martelli 2009-10-28 15:06:10

Never alter the container you're looping on, because iterators on that container are not going to be informed of your alterations and, as you've noticed, that's quite likely to produce a very different loop and/or an incorrect one. In normal cases, looping on a copy of the container helps, but in your case it's clear that you don't want that, as the container will be empty after 50 legs of the loop and if you then try popping again you'll get an exception.

What's anything BUT clear is, what behavior are you trying to achieve, if any?! Maybe you can express your desires with a while...?

i = 0
while i < len(some_list):
    print i,                         
    print some_list.pop(0),                  
    print some_list.pop(0)

@Snowman 2012-05-20 17:10:48

Wait should you increment i in the loop?

@ashes999 2013-03-01 22:25:46

@maq that's not necessary. pop actually removes the element. So you keep looking at element 0 and popping it. Works as intended.

@Aaron Hall 2014-02-24 01:58:18

Alex, I found that this works on trivial examples, should we still avoid this? e.g. >>> l = list('abcdefab') >>> for i in l: if l.count(i) > 1: l.remove(i)

@haccks 2015-03-04 17:16:01

because iterators on that container are not going to be informed of your alterations : Could you please explain why?

@Alex Martelli 2015-03-04 18:59:45

@haccks, because a container doesn't even keep track of iterators that are out on it, much less hook even altering-method to loop over every such iterator and somehow magically let each iterator know about the alterations. It would be a lot subtle, complex code, and checks slowing down very frequent operations. Try coding a list-like container such that, e.g, for i, x in enumerate(container): works perfectly as the loop's body selectively removes some of the container's items, and you'll get a better grasp of the issues -- nested loops next, and, next again, ...:-)

@haccks 2015-03-05 08:47:00

That means once the iterator is yield by the evaluation of l (in OP's code) then any modification to l is not informed to the iterator. This is what for loop reference also says. But when I try to run this code for w in words: print w if len(w) < 6: words.remove(w) print words it gives the output cat defenestrate ['window', 'defenestrate']. Where has 'window' gone? Seems that iterator is informed about the alteration to words.

@Alex Martelli 2015-03-05 14:45:57

@haccks, the words list "slipped one to the left" with each deletion, the iterator was not informed thus kept its internal index into words, then incremented it for the next leg of the loop. So the effect (the way this undefined behavior actually plays out in practice in this case) is that some items of the list happen to be "skipped" in the iteration. Once more: roll your own iterator-on-list class and you'll grasp it better.

@haccks 2015-03-05 16:12:07

the words list "slipped one to the left" with each deletion, ...: Of course that must be the case. But the internal counter/index will keep track of the iterator's items, not of the list items, AFAIK. Am I wrong?

@Alex Martelli 2015-03-05 16:14:12

The iterator doesn't own nor hold any items -- it reaches into the list (at the iterator's current index -- that index is the one thing the iterator owns and holds) each time it needs to supply a value for next (and also increments its internal iterator after that).

@haccks 2015-03-05 16:22:49

Then what is the meaning of this line: The expression list is evaluated once; ? (Actually I am kinda geek and also beginner to python.)

@Alex Martelli 2015-03-05 18:36:16

@haccks, SO is starting to scold for "extended discussions in comments", so please open a new Q if you're burning to know about this -- can't keep chatting in comments on this one, and I have no time for chat rooms. The sentence you quote means exactly what it says: the expression list IS evaluated exactly once. It does NOT mean any copying occurs -- references to the underlying list object (which is mutable but shouldn't be mutated during the loop) reflect whatever changes you make to that list object, right or wrong ones as it may be.

@haccks 2015-03-05 19:00:37

OK. Then I understood that excerpt wrong which means that the iterable object is evaluated only once to create an iterator on it because the for statement calls iter() on the container object. This iterator object pulls out the list items one by one using next() . Any modification to the list will affect this pulling sequence. Your last comment about iterator was really helpful. Thanks for your time.

@Eugene Eeo 2013-10-30 03:54:49

Use a while loop that checks for the truthfulness of the array:

while array:
    value = array.pop(0)
    # do some calculation here

And it should do it without any errors or funny behaviour.

@Tom Leys 2013-12-17 21:53:03

Just make sure that you always pop at least once in every loop.

@York Chang 2013-07-13 05:53:40

I guess this is what you want:

l  = range(100)  
index = 0                       
for i in l:                         
    print i,              
        print l.pop(index+1),                  
        print l.pop(index+1)
    index += 1

It is quite handy to code when the number of item to be popped is a run time decision. But it runs with very a bad efficiency and the code is hard to maintain.

@thethinman 2009-10-28 17:10:20

This slice syntax makes a copy of the list and does what you want:

l  = range(100)  
for i in l[:]:  
    print i,  
    print l.pop(0),  
    print l.pop(0)

@Blckknght 2012-07-27 23:41:47

I'm seeing this very late, but this answer is wrong. The code provided will crash after iterating over 50 items, since it's removing two items from the original list each time through the loop, but not skipping any in the sliced one.

@Hank Gay 2009-10-28 15:01:34

Try this. It avoids mutating a thing you're iterating across, which is generally a code smell.

for i in xrange(0, 100, 3):
    print i

See xrange.

@Ewan Todd 2009-10-28 16:08:26

Python 3.0 range() now behaves like xrange() used to behave, except it works with values of arbitrary size. The latter no longer exists.

@Paul Sasik 2009-10-28 15:01:06

The general rule of thumb is that you don't modify a collection/array/list while iterating over it.

Use a secondary list to store the items you want to act upon and execute that logic in a loop after your initial loop.

Related Questions

Sponsored Content

27 Answered Questions

[SOLVED] How do I check if a list is empty?

  • 2008-09-10 06:20:11
  • Ray
  • 3087535 View
  • 3233 Score
  • 27 Answer
  • Tags:   python list

42 Answered Questions

[SOLVED] How to make a flat list out of list of lists?

22 Answered Questions

[SOLVED] How to iterate over rows in a DataFrame in Pandas

13 Answered Questions

[SOLVED] Iterating over dictionaries using 'for' loops

24 Answered Questions

[SOLVED] How do I concatenate two lists in Python?

31 Answered Questions

[SOLVED] Finding the index of an item in a list

  • 2008-10-07 01:39:38
  • Eugene M
  • 4087248 View
  • 3325 Score
  • 31 Answer
  • Tags:   python list indexing

30 Answered Questions

[SOLVED] What is the best way to iterate over a dictionary?

  • 2008-09-26 18:20:06
  • Jake Stewart
  • 1682774 View
  • 2714 Score
  • 30 Answer
  • Tags:   c# dictionary loops

20 Answered Questions

3 Answered Questions

[SOLVED] Why is printing "B" dramatically slower than printing "#"?

7 Answered Questions

[SOLVED] How does PHP 'foreach' actually work?

Sponsored Content