Overview

In this reading we are going to continue the discussion of loops begun in reading 9, and introduce a new loop type and some new keywords.

Topics

  • while loops

  • infinite loops

  • the break statement

  • the D.R.Y. principle

Loops: beyond for

Previously in Lecture 5 we introduced loops with the for loop. for loops are the most common kind of loop in Python, but they are not the best choice for every situation. For instance:

  • sometimes we’re not working with lists (or even sequences)

  • sometimes we don’t have a fixed number of things to loop over

  • sometimes we don’t know in advance how many times we will have to go through the loop

For these, we will need a different kind of loop.

while loops

Python has a more primitive kind of looping statement called a while loop. It has the following structure:

while <boolean expression>:
    <block of code>

This is syntactically very similar to an if statement: there is a keyword (while) followed by a boolean expression, and a colon character (:) at the end of the line. The rest is a block of code, with each line indented to the same extent. In fact, the only difference between a while loop and an if statement is the use of the keyword while instead of if! The meaning is quite different, though.

while loops evaluate as follows:

  1. The boolean expression is evaluated.

  2. If the boolean expression gives a true (truthy) value, execute the block of code and go back to step 1.

  3. Otherwise, exit the loop.

A simple example

Starting at the number 10, print all the numbers from 10 down to 1:

num = 10
while num > 0:
    print(num)
    num -= 1
print('Done!')

When evaluated, this code prints out:

10
9
8
7
6
5
4
3
2
1
Done!

Notice that when num = 0, the while loop exits and Python continues executing with the line after the while loop (line 5).

A bad example?

This example is unrealistic; you could easily write it with a for loop:

for num in [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]:
    print(num)

When we learn about the range function, we’ll be able to write this even more concisely. To spare you the suspense, here it is:

for num in range(10, 0, -1):
    print(num)

Since it’s so easy to write this code using a for loop, there’s no point is using the more primitive while loop. In fact, doing so would be bad style.

A better example

Problem: use the input function to read numbers from the user and print them, stopping when a negative number is read.

In this case, we cannot know how many times we will have to go through the loop (because we can’t control what the user does!). This is a much more natural situation in which to use a while loop.

Here’s our first attempt:

num = int(input('Enter a number: '))
while num > 0:
    print('Your number was: {}'.format(num))
    num = int(input('Enter a number: '))
print('Done!')

Obviously, this will print out different things every time the code is run. Here’s one sample run:

Enter a number: 2
Your number was: 2
Enter a number: 1729
Your number was: 1729
Enter a number: 2716057
Your number was: 2716057
Enter a number: -91
Done!

(For fun, do a web search on the last three numbers. )

Ugly code

Let’s look at that code again, with line numbers:

num = int(input('Enter a number: '))
while num > 0:
    print('Your number was: {}'.format(num))
    num = int(input('Enter a number: '))
print('Done!')

This code works, but it’s ugly. Why?

It’s because of the repetition. Two of the lines (lines 1 and 4) are exactly the same except for indentation. Writing the same line more than once is very bad style. [1] The problem with repeated code is

  • it’s annoying to read

  • there is almost always a way to write it without repetition

  • if you want to change something in the code, you have to change every repeat or else it probably won’t work

As you get more experience programming, you should develop a personal sense of code aesthetics, and one thing we want you to internalize is that repeated code should make you feel a bit sick to your stomach. It’s ugly, it could be written better, and it will probably cause problems later on.

The D.R.Y. principle

There is a programming principle that summarizes this kind of judgment: the D.R.Y. principle. The acronym D.R.Y. just means Don’t Repeat Yourself. If you find yourself writing the same line, or even almost the same line, more than once, you are violating the D.R.Y. principle and you should consider rewriting your code to making it "DRYer".

However, in order to fix this code, we have to introduce a couple of things in Python we haven’t seen before.

Infinite loops

You almost always want a loop to end. But there is no reason why you can’t have an infinite loop, which is a loop that never ends. In Python, it’s as simple as this:

while True:
    print('This never ends!')

which when run prints out:

This never ends!
This never ends!
This never ends!
This never ends!
This never ends!
This never ends!
This never ends!
This never ends!
...

on and on until you halt it by typing Control-C (the Control key and the c key pressed at the same time). [2]

Infinite loops are not useless, but we do need some way of exiting the loop when some condition is met. Let’s build up to that.

If we re-wrote our loop example using an infinite loop, it might look like this:

while num > 0:
    num = int(input('Enter a number: '))
    print('Your number was: {}'.format(num))

This is both good and bad. On the good side, we’ve gotten rid of the repetition. On the bad side, it never halts! We need to find a way to make it stop.

The break statement

When you want to manually break out of a loop at a particular point, you do this with a break statement. A break statement is just the keyword break, and it means "get out of this loop now!" It’s typically used inside an infinite (while True:) loop. So the phrase "infinite loop" is actually a misnomer; almost always what we really want isn’t an infinite loop but a loop which exits somewhere in the middle of the loop body when some condition is met. This is extremely flexible, though you rarely need it. But this is one of those rare cases!

Let’s look at how we could use this to fix our example:

while True:
    num = int(input('Enter a number: '))
    if num < 0:
        break
    else:
        print('Your number was: {}'.format(num))

Notice that we can’t decide whether to exit the loop until we have the number, since the exit condition is that the number is < 0. But in order to get the number, we have to use the input function. If we don’t want to repeat the input line, it has to be inside the loop. This means that we can only choose whether to exit the loop when we are part way through executing the loop body. This is a typical way of using a break statement.

Notice that this also removed the repetition from our code, so we have "DRYed" up the code. This is objectively better code; for instance, if we wanted to change the prompt, we would only have to do it in one place. Repeated code is almost always bad code.

Actually, we can simplify the code a bit more:

while True:
    num = int(input('Enter a number: '))
    if num < 0:
        break
    print('Your number was: {}'.format(num))

There is no need for the else, because if the break ever executes, the loop terminates, so if you get past the break you are already in the "else" case. In general, you shouldn’t have an else if it isn’t necessary. Even more generally, you shouldn’t have any code that isn’t necessary.

When to use break (rules of thumb)

  • A regular while loop (without break) works well when we can test for the loop exit condition before we start executing the loop body.

  • If you need to check for the exit condition in the middle or end of the loop body you probably want to have an infinite loop (while True:) with a break statement in the loop body. [3]

If you don’t mind repeating code, you can always rewrite a loop so it doesn’t need a break, but it’s usually much easier to understand code that uses a break than code with repetition. So, when necessary: give yourself a break! [4]

It’s also important to realize that the D.R.Y. principle is a rule of thumb; it’s a guideline that will generally steer you towards better code. Like every rule of thumb, though, there may be exceptions. Sometimes, a little repetition may be better than trying to write the D.R.Y.-est possible code. As you gain experience with programming, you will develop an intuition about what good code is and what it isn’t.

returning from inside a loop

Sometimes you can get away with using a return statement instead of a break statement. This happens when you are in a function and you are looking for a particular value; when you find it, you return from the entire function (which implies that you also exit the loop). Here’s a function which is somewhat similar to our previous example but which uses return instead of break to exit a while loop:

def get_number():
    """Get a non-negative integer from the terminal."""
    while True:
        num = int(input('Enter a number: '))
        if num > 0:
            return num

An interaction with get_number might look like this:

>>> get_number()
Enter a number: -10
Enter a number: -42
Enter a number: 55
55

In this case, the while loop will continue until the user inputs a non-negative integer. [5] Once that happens, the return will exit the function, so you don’t need a break. You could use a break, like this:

def get_number():
    """Get a non-negative integer from the terminal."""
    while True:
        num = int(input('Enter a number: '))
        if num > 0:
            break
    return num

This will do the same thing, but it’s more code.

More about break

For completeness, we should mention a couple of other things about break:

  • You aren’t required to use while True: in order to use break. You can use break inside any while loop. Sometimes this is useful if there is a (rare) condition where you can exit the loop early.

  • You can also use break from inside a for loop as well as a while loop.

  • A break statement only exits the nearest enclosing loop. If you have a nested loop (say, a for loop inside another for loop), the break will only exit the innermost loop that contains it. If you want to break out of more than one loop, you have to do it a different way. [6]

Looking forward

There is another loop-related keyword called continue which we will discuss in a few readings. It’s rarely used but (again) can make code DRYer in some cases.


[End of reading]


1. Some authors call it a "code smell" because it’s "stinky".
2. Control-C is a common way to halt runaway processes, at least on Unix-based systems like Linux and Mac OS X.
3. Many languages like C and Java have a do/while loop which is used when the test comes at the end of the loop. Python doesn’t have this kind of loop.
4. If you think this will be the last bad joke in the readings, think again.
5. Or until the user inputs a non-integer, which will result in an error.
6. The usual way of doing this involves raising an exception and then catching it outside of all the loops you want to exit. We will learn about exceptions later on in the course.