Chapter 10. while and for Loops

In this chapter, we meet Python’s two main looping constructs—statements that repeat an action over and over. The first of these, the while loop, provides a way to code general loops; the second, the for statement, is designed for stepping through the items in a sequence object and running a block of code for each item.

There are other kinds of looping operations in Python, but the two statements covered here are the primary syntax provided for coding repeated actions. We’ll also study a few unusual statements such as break and continue here, because they are used within loops.

while Loops

Python’s while statement is its most general iteration construct. In simple terms, it repeatedly executes a block of indented statements, as long as a test at the top keeps evaluating to a true value. When the test becomes false, control continues after all the statements in the while block; the body never runs if the test is false to begin with.

The while statement is one of two looping statements (along with the for). It is called a loop because control keeps looping back to the start of the statement, until the test becomes false. The net effect is that the loop’s body is executed repeatedly while the test at the top is true. Besides statements, Python also provides a handful of tools that implicitly loop (iterate): the map, reduce, and filter functions; the in membership test; list comprehensions; and more. We’ll explore most of these in Chapter 14.

General Format

In its most complex form, the while statement consists of a header line with a test expression, a body of one or more indented statements, and an optional else part that is executed if control exits the loop without running into a break statement. Python keeps evaluating the test at the top, and executing the statements nested in the while part, until the test returns a false value:

while <test>:             # Loop test
    <statements1>         # Loop body
else:                     # Optional else
    <statements2>         # Run if didn't exit loop with break

Examples

To illustrate, here are a few of simple while loops in action. The first just prints a message forever, by nesting a print statement in a while loop. Recall that an integer 1 means true; since the test is always true, Python keeps executing the body forever or until you stop its execution. This sort of behavior is usually called an infinite loop:

>>> while 1:
...    print 'Type Ctrl-C to stop me!'

The next example keeps slicing off the first character of a string, until the string is empty and hence false. It’s typical to test an object directly like this, instead of using the more verbose equivalent: while x != '':. Later in this chapter, we’ll see other ways to step more directly through the items in a string with a for.

>>> x = 'spam'
>>> while x:
...     print x,
...     x = x[1:]      # Strip first character off x.
...
spam pam am m

The code below counts from the value of a, up to but not including b. We’ll see an easier way to do this with a Python for and range later.

>>> a=0; b=10
>>> while a < b:       # One way to code counter loops
...     print a,
...     a += 1         # Or, a = a+1
...
0 1 2 3 4 5 6 7 8 9

break, continue, pass, and the Loop else

Now that we’ve seen our first Python loop, we should introduce two simple statements that have a purpose only when nested inside loops—the break and continue statements. We will also study the loop else clause here because it is intertwined with break, and Python’s empty placeholder statement, the pass. In Python:

break

Jumps out of the closest enclosing loop (past the entire loop statement)

continue

Jumps to the top of the closest enclosing loop (to the loop’s header line)

pass

Does nothing at all: it’s an empty statement placeholder

Loop else block

Runs if and only if the loop is exited normally—without hitting a break

General Loop Format

Factoring in break and continue statements, the general format of the while loop looks like this:

while <test1>:
    <statements1>
    if <test2>: break         # Exit loop now, skip else.
    if <test3>: continue      # Go to top of loop now.
else:
    <statements2>             # If we didn't hit a 'break'

break and continue statements can appear anywhere inside the while (and for) loop’s body, but they are usually coded further nested in an if test, to take action in response to some sort of condition.

Examples

Let’s turn to a few simple examples to see how these statements come together in practice. The pass statement is used when the syntax requires a statement, but you have nothing useful to say. It is often used to code an empty body for a compound statement. For instance, if you want to code an infinite loop that does nothing each time through, do it with a pass:

while 1: pass   # Type Ctrl-C to stop me!

Since the body is just an empty statement, Python gets stuck in this loop.[1] pass is roughly to statements as None is to objects—an explicit nothing. Notice that the while loop’s body is on the same line as the header, after the colon; as in the if, this only works if the body isn’t a compound statement.

The continue statement is an immediate jump to the top of a loop. It sometimes lets you avoid statement nesting; the next example uses continue to skip odd numbers. This code prints all even numbers less than 10 and greater than or equal to 0. Remember, 0 means false, and % is the remainder-of-division operator, so this loop counts down to zero, skipping numbers that aren’t multiples of two (it prints 8 6 4 2 0):

x = 10
while x:
    x = x-1                   # Or, x -= 1
    if x % 2 != 0: continue   # Odd?--skip print
    print x,

Because continue jumps to the top of the loop, you don’t need to nest the print statement inside an if test; the print is only reached if the continue is not run. If this sounds similar to a “goto” in other languages it should; Python has no goto statement, but because continue lets you jump about in a program, many of the warnings about readability and maintainability you may have heard about goto apply. continue should probably be used sparingly, especially when you’re first getting started with Python; the example above, for instance, might be clearer if the print were nested under the if:

x = 10
while x:
    x = x-1
    if x % 2 == 0:            # Even?-- print
        print x,

The break statement is an immediate loop exit. Because code below it is never reached, the break can also sometimes avoid nesting. For example, here is a simple interactive loop, which inputs data with raw_input, and exits when the user enters “stop” for the name request:

>>> while 1:
...     name = raw_input('Enter name:')
...     if name == 'stop': break
...     age  = raw_input('Enter age: ')
...     print 'Hello', name, '=>', int(age) ** 2
...
Enter name:mel
Enter age: 40
Hello mel => 1600
Enter name:bob
Enter age: 30
Hello bob => 900
Enter name:stop

Notice how this code converts the age input to an integer before raising it to the second power, with int; raw_input returns user input as a string. In Chapter 22, you’ll see that it also raises an exception on end-of-file (e.g., if users type Ctrl-Z or Ctrl-D); if it matters, wrap raw_input in try statements.

When combined with the loop else, the break statement can often eliminate the search status flags used in other languages. For instance, the following piece of code determines if a positive integer number y is prime by searching for factors greater than 1:

x = y / 2                          # For some y > 1
while x > 1:
    if y % x == 0:                 # Remainder
        print y, 'has factor', x
        break                      # Skip else
    x = x-1
else:                              # Normal exit
    print y, 'is prime'

Rather than setting a flag to be tested when the loop is exited, insert a break where a factor is found. This way, the loop else can assume that it will be executed only if no factor was found; if you don’t hit the break, the number is prime.[2]

The loop else is also run if the body of the loop is never executed, since you don’t run a break in that event either; in a while loop, this happens if the test in the header is false to begin with. In the example above, you still get the “is prime message” if x is initially less than or equal to 1 (e.g., if y is 2).

More on the Loop else

Because the loop else clause is unique to Python, it tends to perplex some newcomers on first glance. In more general terms, the loop else provides explicit syntax for a common coding scenario—it is a coding structure that lets you catch the “other” way out of a loop, without setting and checking flags or conditions.

Suppose, for instance, that you are writing a loop to search a list for a value, and need to know whether the value was found after you exit the loop. You might code such a task this way:

found = 0
while x and not found:
    if match(x[0]):              # Value at front?
        print 'Ni'
        found = 1
    else:
        x = x[1:]                # Slice off front and repeat.
if not found:
    print 'not found'

Here, we initialize, set, and later test a flag, to know if the search succeeded or not. This is valid Python code, and does work; but it’s exactly the sort of structure that the loop else is there to handle. Here’s an else equivalent:

while x:                         # Exit when x empty.
    if match(x[0]):
        print 'Ni'
        break                    # Exit, go around else.
    x = x[1:]
else:
    print 'Not found'            # Only here if exhausted x.

Here, the flag is gone, and we replaced the if test at loop end with an else (lined up vertically with the word while, by indentation). Because the break inside the main part of the while exits the loop and goes around the else, this serves as a more structured way to catch the search failure case.

Some readers might notice that the prior example’s else could be replaced with a test for an empty x after the loop (e.g., if not x:). Although that’s true in this example, the else provides explicit syntax for this coding pattern (it’s more obviously a search failure clause here), and such an explicit empty test may not apply in some cases. Moreover, the loop else becomes even more useful when used in conjunction with the for loop, because sequence iteration is not under your control.

for Loops

The for loop is a generic sequence iterator in Python: it can step through the items in any ordered sequence object. The for works on strings, lists, tuples, and new objects we’ll create later with classes.

General Format

The Python for loop begins with a header line that specifies an assignment target (or targets), along with an object you want to step through. The header is followed by a block of indented statements, which you want to repeat:

for <target> in <object>:   # Assign object items to target.
    <statements>            # Repeated loop body: use target
else:
    <statements>            # If we didn't hit a 'break'

When Python runs a for loop, it assigns items in the sequence object to the target, one by one, and executes the loop body for each. The loop body typically uses the assignment target to refer to the current item in the sequence, as though it were a cursor stepping through the sequence.

The name used as the assignment target in a for header line is usually a (possibly new) variable in the scope where the for statement is coded. There’s not much special about it; it can even be changed inside the loop’s body, but will be automatically set to the next item in the sequence when control returns to the top of the loop again. After the loop, this variable normally still refers to the last item visited, which is the last item in the sequence unless the loop exits with a break statement.

The for also supports an optional else block, which works exactly as it does in while loops; it’s executed if the loop exits without running into a break statement (i.e., if all items in the sequence were visited). In fact, the break and continue statements introduced above work the same in the for loop as they do in the while. The for loop’s complete format can be described this way:

for <target> in <object>:   # Assign object items to target.
    <statements>
    if <test>: break        # Exit loop now, skip else.
    if <test>: continue     # Go to top of loop now.
else:
    <statements>            # If we didn't hit a 'break'

Examples

Let’s type a few for loops interactively. In the first example, the name x is assigned to each of the three items in the list in turn, from left to right, and the print statement is executed for each. Inside the print statement (the loop body), the name x refers to the current item in the list:

>>> for x in ["spam", "eggs", "ham"]:
...     print x,
...
spam eggs ham

The next two examples compute the sum and product of all the items in a list. In the next chapter, we’ll see built-ins that apply operations like + and * to items in a list automatically, but it’s usually just as easy to use a for:

>>> sum = 0
>>> for x in [1, 2, 3, 4]:
...     sum = sum + x
...
>>> sum
10
>>> prod = 1
>>> for item in [1, 2, 3, 4]: prod *= item
...
>>> prod
24

For loops work on strings and tuples too—any sequence works in a for:

>>> S, T = "lumberjack", ("and", "I'm", "okay")

>>> for x in S: print x,
...
l u m b e r j a c k

>>> for x in T: print x,
...
and I'm okay

If you’re iterating through a sequence of tuples, the loop target can actually be a tuple of targets. This is just another case of tuple unpacking assignment at work; remember, the for assigns items in the sequence to the target, and assignment works the same everywhere:

>>> T = [(1, 2), (3, 4), (5, 6)]
>>> for (a, b) in T:                   # Tuple assignment at work
...     print a, b
...
1 2
3 4
5 6

Here, the first time through the loop, it’s like writing: (a,b) = (1,2); the second time (a,b) is assigned (3,4), and so on. This isn’t a special case; any assignment target works syntactically after the word for.

Now, let’s look at something a bit more sophisticated. The next example illustrates both the loop else in a for and statement nesting. Given a list of objects (items) and a list of keys (tests), this code searches for each key in the objects list, and reports on the search’s outcome:

>>> items = ["aaa", 111, (4, 5), 2.01]          # A set of objects
>>> tests = [(4, 5), 3.14]                      # Keys to search for
>>>
>>> for key in tests:                           # For all keys
...     for item in items:                      # For all items
...         if item == key:                     # Check for match.
...             print key, "was found"
...             break
...     else:
...         print key, "not found!"
...
(4, 5) was found
3.14 not found!

Since the nested if runs a break when a match is found, the loop else can assume that the search has failed. Notice the nesting here: when this code runs, there are two loops going at the same time. The outer loop scans the keys list, and the inner loop scans the items list for each key. The nesting of the loop else is critical; it’s indented at the same level as the header line of the inner for loop, so it’s associated with the inner loop (not the if or outer for).

By the way, this example is easier to code if we employ the in operator to test membership. Since in implicitly scans a list looking for a match, it replaces the inner loop:

>>> for key in tests:                    # For all keys
...     if key in items:                 # Let Python check for a match.
...         print key, "was found"
...     else:
...         print key, "not found!"
...
(4, 5) was found
3.14 not found!

In general, it’s a good idea to let Python do as much of the work as possible, for the sake of both brevity and performance. The next example performs a typical data-structure task with a for—collecting common items in two sequences (strings). It’s roughly a simple set intersection routine; after the loop runs, res refers to a list that contains all the items found in both seq1 and seq2:

>>> seq1 = "spam"
>>> seq2 = "scam"
>>>
>>> res = [  ]                     # Start empty.
>>> for x in seq1:               # Scan first sequence.
...     if x in seq2:            # Common item?
...         res.append(x)        # Add to result end.
...
>>> res
['s', 'a', 'm']

Unfortunately, this code is equipped to work only on two specific variables: seq1 and seq2. It would be nice if this loop could be somehow generalized into a tool you could use more than once. As you’ll see, that simple idea leads us to functions, the topic of Part IV.

Loop Variations

The for loop subsumes most counter-style loops. It’s generally simpler to code and quicker to run than a while, so it’s the first tool you should reach for whenever you need to step through a sequence. But there are also situations where you will need to iterate in a more specialized way. For example, what if you need to visit every second or third item in a list, or change the list along the way? How about traversing more than one sequence in parallel, in the same for loop?

You can always code such unique iterations with a while loop and manual indexing, but Python provides two built-ins that allow you to specialize the iteration in a for:

  • The built-in range function returns a list of successively higher integers, which can be used as indexes in a for.[3]

  • The built-in zip function returns a list a parallel-item tuples, which can be used to traverse multiple sequences in a for.

Let’s look at each of these built-ins in turn.

Counter Loops: range

The range function is really independent of for loops; although it’s used most often to generate indexes in a for, you can use it anywhere you need a list of integers:

>>> range(5), range(2, 5), range(0, 10, 2)
([0, 1, 2, 3, 4], [2, 3, 4], [0, 2, 4, 6, 8])

With one argument, range generates a list with integers from zero up to but not including the argument’s value. If you pass in two arguments, the first is taken as the lower bound. An optional third argument can give a step; if used, Python adds the step to each successive integer in the result (steps default to one). Ranges can also be nonpositive, and nonascending, if you want them to be:

>>> range(-5, 5)
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]

>>> range(5, -5, -1)
[5, 4, 3, 2, 1, 0, -1, -2, -3, -4]

Although such range results may be useful all by themselves, they tend to come in most handy within for loops. For one thing, they provide a simple way to repeat an action a specific number of times. To print three lines, for example, use a range to generate the appropriate number of integers:

>>> for i in range(3):
...     print i, 'Pythons'
...
0 Pythons
1 Pythons
2 Pythons

range is also commonly used to iterate over a sequence indirectly. The easiest and fastest way to step through a sequence exhaustively is always with a simple for; Python handles most of the details for you:

>>> X = 'spam'
>>> for item in X: print item,            # Simple iteration
...
s p a m

Notice the trailing comma on the print statement here, to suppress the default line feed (each print keeps adding to the current output line). Internally, the for handles the details of the iteration automatically. If you really need to take over the indexing logic explicitly, you can do it with a while loop:

>>> i = 0
>>> while i < len(X):                     # while loop iteration
...     print X[i],; i += 1
...
s p a m

You can also do manual indexing with a for, if you use range to generate a list of indexes to iterate through:

>>> X
'spam'
>>> len(X)                                # Length of string
4
>>> range(len(X))                         # All legal offsets into X
[0, 1, 2, 3]
>>>
>>> for i in range(len(X)): print X[i],   # Manual for indexing
...
s p a m

The example here is stepping over a list of offsets into X, not the actual items of X; we need to index back into X within the loop to fetch each item.

Nonexhaustive Traversals: range

The last example of the prior section works, but it probably runs more slowly than it has to. Unless you have a special indexing requirement, you’re always better off using the simple for loop form in Python—use for instead of while whenever possible, and don’t resort to range calls in for loops except as a last resort.

However, the same coding pattern used in that prior example also allows us to do more specialized sorts of traversals:

>>> S = 'abcdefghijk'
>>> range(0, len(S), 2)
[0, 2, 4, 6, 8, 10]

>>> for i in range(0, len(S), 2): print S[i],
...
a c e g i k

Here, we visit every second item in string S, by stepping over the generated range list. To visit every third item, change the third range argument to be 3, and so on. In effect, range used this way lets you skip items in loops, while still retaining the simplicity of the for. See also Python 2.3’s new optional third slice limit, in Section 5.2.2 in Chapter 5. In 2.3, a similar effect may be achieved with:

for x in S[::2]: print x

Changing Lists: range

Another common place you may use range and for combined is in loops that change a list as it is being traversed. The following example needs an index to be able to assign an updated value to each position as we go:

>>> L = [1, 2, 3, 4, 5]
>>>
>>> for i in range(len(L)):          # Add one to each item in L
...     L[i] += 1                    # Or L[i] = L[i] + 1
...
>>> L
[2, 3, 4, 5, 6]

There is no way to do the same with a simple for x in L: style loop here, because such a loop iterates through actual items, not list positions. The equivalent while requires a bit more work on our part:[4]

>>> i = 0
>>> while i < len(L):
...     L[i] += 1
...     i += 1
...
>>> L
[3, 4, 5, 6, 7]

Parallel Traversals: zip and map

The range trick traverses sequences with for in nonexhaustive fashion. The built-in zip function allows us to use for loops to visit multiple sequences in parallel. In basic operation, zip takes one or more sequences, and returns a list of tuples that pair up parallel items taken from its arguments. For example, suppose we’re working with two lists:

>>> L1 = [1,2,3,4]
>>> L2 = [5,6,7,8]

To combine the items in these lists, we can use zip:

>>> zip(L1,L2)
[(1, 5), (2, 6), (3, 7), (4, 8)]

Such a result may be useful in other contexts. When wedded with the for loop, though, it supports parallel iterations:

>>> for (x,y) in zip(L1, L2):
...     print x, y, '--', x+y
...
1 5 -- 6
2 6 -- 8
3 7 -- 10
4 8 -- 12

Here, we step over the result of the zip call—the pairs of items pulled from the two lists. This for loop uses tuple assignment again to unpack each tuple in the zip result (the first time through, it’s as though we run (x,y)=(1,5)). The net effect is that we scan both L1 and L2 in our loop. We could achieve a similar effect with a while loop that handles indexing manually, but it would be more to type, and may be slower than the for/zip approach.

The zip function is more general than this example suggests. For instance, it accepts any type of sequence, and more than two arguments:

>>> T1, T2, T3 = (1,2,3), (4,5,6), (7,8,9)
>>> T3
(7, 8, 9)
>>> zip(T1,T2,T3)
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]

zip truncates result tuples at the length of the shortest sequence, when argument lengths differ:

>>> S1 = 'abc'
>>> S2 = 'xyz123'
>>>
>>> zip(S1, S2)
[('a', 'x'), ('b', 'y'), ('c', 'z')]

The related, and older, built-in map function pairs items from sequences in a similar fashion, but pads shorter sequences with None if argument lengths differ:

>>> map(None, S1, S2)
[('a', 'x'), ('b', 'y'), ('c', 'z'), (None, '1'), (None, '2'), (None,'3')]

The example is actually using a degenerate form of the map built-in. Normally, map takes a function, and one or more sequence arguments, and collects the results of calling the function with parallel items taken from the sequences. When the function argument is None (as here), it simply pairs items like zip. map and similar function-based tools are covered in Chapter 11.

Dictionary Construction with zip

Dictionaries can always be created by coding a dictionary literal, or assigning to keys over time:

>>> D1 = {'spam':1, 'eggs':3, 'toast':5}
>>> D1
{'toast': 5, 'eggs': 3, 'spam': 1}

>>> D1 = {  }
>>> D1['spam']  = 1
>>> D1['eggs']  = 3
>>> D1['toast'] = 5

What to do, though, if your program obtains dictionary keys and values in lists at runtime, after you’ve coded your script?

>>> keys = ['spam', 'eggs', 'toast']
>>> vals = [1, 3, 5]

One solution to go from the lists to a dictionary is to zip the lists and step through them in parallel with a for loop:

>>> zip(keys, vals)
[('spam', 1), ('eggs', 3), ('toast', 5)]

>>> D2 = {  }
>>> for (k, v) in zip(keys, vals): D2[k] = v
...
>>> D2
{'toast': 5, 'eggs': 3, 'spam': 1}

It turns out, though, that you can skip the for loop altogether, and simply pass the zipped keys/values lists to the built-in dict constructor call in Python 2.2:

>>> keys = ['spam', 'eggs', 'toast']
>>> vals = [1, 3, 5]

>>> D3 = dict(zip(keys, vals))
>>> D3
{'toast': 5, 'eggs': 3, 'spam': 1}

The built-in name dict is really a type name in Python; calling it is something like a list-to-dictionary conversion, but really is an object construction request (more about type names in Chapter 23). Also, in Chapter 14, we’ll meet a related but richer concept, the list comprehension, which builds lists in a single expression.



[1] This code does nothing, forever. It probably isn’t the most useful Python program ever written, unless you want to test a CPU meter, or warm up your laptop computer on a cold winter’s day. Frankly, though, we couldn’t think of a better pass example. We’ll see other places where it makes sense later in the book (for instance, in Chapter 22, to define empty classes that implement objects that behave like “structs” and “records” in other languages).

[2] More or less. Numbers less than 2 are not considered prime by the strict mathematical definition. To be really picky, this code also fails for negatives, fails for floating-point numbers, and will be broken by the future / “true” division change mentioned in Chapter 4. If you want to experiment with this code, be sure to see the exercise at the end of Part IV, which wraps it in a function.

[3] Python also provides a built-in called xrange that generates indexes one at a time instead of storing all of them in a list at once like range does. There’s no speed advantage to xrange, but it’s useful as a space optimization if you have to generate a huge number of values.

[4] A list comprehension expression of the form [x+1 for x in L] would do similar work here as well, albeit without changing the original list in-place (we could assign the expression’s new list object result back to L, but this would not update any other references to the original list). See Chapter 14 for more on list comprehensions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.22.27.45