One of the interesting things about helping beginning programmers is to see the way they think. After programming for so long, and using Python for so long, it's hard to remember how confusing it can all be. Beginners can reacquaint us with the difficulties.

Python has a handy way to iterate over all the elements of a sequence, such as a list:

for x in seq:
    doit(x)

But if you've only learned a few basic things, or are coming from a language like C or Javascript, you might do it like this:

i = 0
while i < len(seq):
    x = seq[i]
    doit(x)
    i += 1

(BTW, I did a talk at the PyCon before last all about iteration in Python, including these sorts of comparisons of techniques: Loop Like a Native.)

Once you learn about the range() builtin function, you know you can loop over the indexes of the sequence like this:

for i in range(len(seq)):
    x = seq[i]
    doit(x)

These two styles of loop are commonly seen. But when I saw this on Stackoverflow, I did a double-take:

i = 0
while i in range(len(seq)):
    x = seq[i]
    doit(x)
    i += 1

This is truly creative! It's an amalgam of the two beginner loops we've already seen, and at first glance, looks like a syntax error.

In fact, this works in both Python 2 and Python 3. In Python 2, range() produces a list, and lists support the "in" operator for checking element membership. In Python 3, range() produces a range object which also supports "in".

So each time around the loop, a new range is constructed, and it's examined for the value of i. It works, although it's baroque and performs poorly in Python 2, being O(n2) instead of O(n).

People are creative! Just when I thought there's no other ways to loop over a list, a new technique arrives!

tagged: » 8 reactions

Comments

[gravatar]
Matt 12:15 PM on 30 Jul 2014

"So each time around the loop, a new range is constructed, and it's examined for the value of i."

Are you sure about that? What python version? In my experimentations, the range is constructed just once.

[gravatar]
Marc 1:42 PM on 30 Jul 2014

If you need the index inside the for loop, another way is using enumerate():

for i, x in enumarate(seq):
    doit(seq[i])
Just another way to do it (-:

[gravatar]
Ned Batchelder 2:01 PM on 30 Jul 2014

@Matt the expression in the while statement is evaluated each time around the loop. That expression calls range(), which will make a new range object. How are you determining that it's the same object?

[gravatar]
AK 2:20 PM on 30 Jul 2014

I thought what Matt said was the case as well, since this appears to be the case in for loops. I ran an experiment to see for myself:

>>> def prange(x):
...     print("Ran prange")
...     return range(x)
... 
>>> def plen(obj):
...     print("Ran plen")
...     return len(obj)
... 
>>> i = 0
>>> seq = [1,2,3,4,5]
>>> while i in prange(plen(seq)):
...     x = seq[i]
...     print("Loop" + str(i))
...     i += 1
...
Ran plen
Ran prange
Loop 0
Ran plen
Ran prange
Loop 1
Ran plen
Ran prange
Loop 2
Ran plen
Ran prange
Loop 3
Ran plen
Ran prange
Loop 4
Ran plen
Ran prange
>>> for i in prange(plen(seq)):
...     print("Loop " + str(i))
... 
Ran plen
Ran prange
Loop 0
Loop 1
Loop 2
Loop 3
Loop 4
So it does indeed appear that in this while loop construction, the functions are evaluated at the beginning of every loop. Note, this is using Python 2.7.5, maybe it's different in 3?

[gravatar]
Ned Batchelder 2:27 PM on 30 Jul 2014

@AK: for loops and while loops are different. In a for loop, the expression is evaluated once to get an iterator. Then the body of the loop is executed once for each value pulled from the iterator. In a while loop, the expression is evaluated each time around the loop. It's the same in all versions of Python.

[gravatar]
Ned Batchelder 2:29 PM on 30 Jul 2014

One of the things that makes this code so confusing is that "while i in range(len(seq))" looks almost identical to "for i in range(len(seq))", the only difference is for and while.

But the for-statement syntax is "for VAR in EXPR:", and the while-statement syntax is "while EXPR:". The "in" keyword is part of the syntax of the for-statement, but part of the expression in the while-statement.

[gravatar]
AK 2:34 PM on 30 Jul 2014

Ah okay, interesting, thanks for the clarification!

[gravatar]
Matt 2:42 PM on 30 Jul 2014

... argh ... silly typo on my part. You are right! Thanks for responding.

Add a comment:

name
email
Ignore this:
not displayed and no spam.
Leave this empty:
www
not searched.
 
Name and either email or www are required.
Don't put anything here:
Leave this empty:
URLs auto-link and some tags are allowed: <a><b><i><p><br><pre>.