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.
Related Questions
Sponsored Content
39 Answered Questions
[SOLVED] How to make a flat list out of list of lists?
- 2009-06-04 20:30:05
- Emma
- 1402274 View
- 2430 Score
- 39 Answer
- Tags: python list multidimensional-array flatten
36 Answered Questions
26 Answered Questions
[SOLVED] Difference between append vs. extend list methods in Python
- 2008-10-31 05:55:36
- Claudiu
- 2455286 View
- 2978 Score
- 26 Answer
- Tags: python list data-structures append extend
28 Answered Questions
14 Answered Questions
[SOLVED] What is the meaning of a single and a double underscore before an object name?
- 2009-08-19 17:11:57
- Ram Rachum
- 319350 View
- 1075 Score
- 14 Answer
- Tags: python naming-conventions underscores double-underscore
30 Answered Questions
[SOLVED] How to concatenate two lists in Python?
- 2009-11-12 07:04:09
- y2k
- 1710016 View
- 1911 Score
- 30 Answer
- Tags: python list concatenation
7 Answered Questions
[SOLVED] How does PHP 'foreach' actually work?
- 2012-04-07 19:33:57
- DaveRandom
- 359577 View
- 1779 Score
- 7 Answer
- Tags: php loops foreach iteration php-internals
10 Answered Questions
[SOLVED] Why is reading lines from stdin much slower in C++ than Python?
- 2012-02-21 02:17:50
- JJC
- 224344 View
- 1621 Score
- 10 Answer
- Tags: python c++ benchmarking iostream getline
7 Answered Questions
2 Answered Questions
[SOLVED] Why is printing "B" dramatically slower than printing "#"?
- 2014-02-21 23:45:43
- Kuba Spatny
- 220278 View
- 2536 Score
- 2 Answer
- Tags: java performance loops for-loop system.out
7 comments
@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
...?@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 element0
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 tol
is not informed to the iterator. This is whatfor
loop reference also says. But when I try to run this codefor w in words: print w if len(w) < 6: words.remove(w) print words
it gives the outputcat defenestrate ['window', 'defenestrate']
. Where has'window'
gone? Seems that iterator is informed about the alteration towords
.@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 intowords
, 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 callsiter()
on the container object. This iterator object pulls out the list items one by one usingnext()
. 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:
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:
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:
@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.
@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.Other points about my example relate to new syntax in python 3.0.
@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 evenxrange(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)] ?
@alfasin 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)].
@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.
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.