Iteration

Repetition, Repetition, Repetition, Repe…

For Loops

We’ve seen simple iteration over a sequence with for ... in:

In [170]: for x in "a string":
   .....:     print(x)
   .....:
a

s
t
r
i
n
g

No Indexing Required

Contrast this with other languages, where you must build and use an index to iterate through an array.

for(var i = 0; i < arr.length; i++) {
    var value = arr[i];
    alert(i + ") " + value);

If you do need an index, you can use enumerate:

In [140]: for idx, letter in enumerate('Python'):
   .....:     print(idx, letter, end=' ')
   .....:
0 P 1 y 2 t 3 h 4 o 5 n

range and for Loops

The range builtin is useful for looping a known number of times:

In [171]: for i in range(5):
   .....:     print(i)
   .....:
0
1
2
3
4

But you don’t really need to do anything at all with i

In fact, it’s a common convention to make this clear with a “nothing” name:

In [21]: for __ in range(5):
   ....:     print("*")
   ....:
*
*
*
*
*

No Namespace

Be alert that a loop does not create a local namespace:

In [172]: x = 10
In [173]: for x in range(3):
   .....:     pass
   .....:
In [174]: x
Out[174]: 2

Loop Control

Sometimes you want to interrupt or alter the flow of control through a loop.

Loops can be controlled in two ways, with break and continue.

The break keyword will cause a loop to immediately terminate:

In [141]: for i in range(101):
   .....:     print(i)
   .....:     if i > 50:
   .....:         break
   .....:
0 1 2 3 4 5... 46 47 48 49 50 51

The continue keyword will skip later statements in the loop block, but allow iteration to continue:

In [143]: for in in range(101):
   .....:     if i > 50:
   .....:         break
   .....:     if i < 25:
   .....:         continue
   .....:     print(i, end=' ')
   .....:
   25 26 27 28 29 ... 41 42 43 44 45 46 47 48 49 50

Take some time to look at these examples carefully, and make sure you understand them. It’s probably a good idea to write a bit of code to experiment as well.

else

For loops can also take an optional else block.

This is not a feature of most languages, but it can be handy.

Executed only when the loop exits normally (not via break):

In [147]: for x in range(10):
   .....:     if x == 11:
   .....:         break
   .....: else:
   .....:     print('finished')
finished
In [148]: for x in range(10):
   .....:     if x == 5:
   .....:         print(x)
   .....:         break
   .....: else:
   .....:     print('finished')
5

This is a really nice, unique Python feature!

If Python didn’t have else on loops, you’d need to set a flag, something like:

it_did_break = False
for x in range(10):
    if x == 11:
        it_did_break = True
        break
if not it_did_break:
    print('finished')

That’s klunkier, no?

Make sure to try this a bit yourself too, to make sure you get it.

While Loops

While loops are different – they are not for iterating over a collection, but rather for repeating something an unknown number of times – and maybe even forever – or until the program terminates.

The while keyword is for when you don’t know how many loops you need.

It continues to execute the body until the associated condition does not evaluate to True:

while a_condition:
   some_code
   in_the_body

while vs. for

while is more general than for

– you can always express for as while, but not always vice-versa.

while is more error-prone – requires some care to terminate.

The loop body must make progress, so the associated condition can become False.

Care must be taken to avoid an unintended error – infinite loops:

i = 0;
while i < 5:
    print(i)

Terminating a while Loop

Use break:

In [150]: while True:
   .....:     i += 1
   .....:     if i > 10:
   .....:         break
   .....:     print(i)
   .....:
1 2 3 4 5 6 7 8 9 10

Set a flag:

In [156]: import random
In [157]: keep_going = True
In [158]: while keep_going:
   .....:     num = random.choice(range(5))
   .....:     print(num)
   .....:     if num == 3:
   .....:         keep_going = False
   .....:
3

Use a condition:

In [161]: while i < 10:
   .....:     i += random.choice(range(4))
   .....:     print(i)
   .....:
0 0 2 3 4 6 8 8 8 9 12

Similarities

Both for and while loops can use break and continue for internal flow control.

Both for and while loops can have an optional else block.

In both loops, the statements in the else block are only executed if the loop terminates normally (no break).

Pythonic Iteration

I’ve already said it, but it bears repeating:

for loops are for iterating over something (an “iterable”) – you almost never want to iterate over the indexes, and then access items with the index.

Nifty for loop tricks

tuple unpacking:

remember this?

x, y = 3, 4

You can do that in a for loop, also:

In [4]: l = [(1, 2), (3, 4), (5, 6)]

In [5]: for i, j in l:
            print("i:{}, j:{}".format(i, j))

i:1, j:2
i:3, j:4
i:5, j:6

Looping through two iterables at once:

zip
In [10]: l1 = [1, 2, 3]

In [11]: l2 = [3, 4, 5]

In [12]: for i, j in zip(l1, l2):
          print("i:{}, j:{}".format(i, j))

i:1, j:3
i:2, j:4
i:3, j:5

There can be more than two:

for i, j, k, l in zip(l1, l2, l3, l4):

Need the index and the item?

enumerate
In [2]: l = ['this', 'that', 'the other']

In [3]: for i, item in enumerate(l):
   ...:     print("the {:d}th item is: {:s}".format(i, item))
   ...:
the 0th item is: this
the 1th item is: that
the 2th item is: the other