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:
-
The boolean expression is evaluated.
-
If the boolean expression gives a true (truthy) value, execute the block of code and go back to step 1.
-
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 (withoutbreak
) 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 abreak
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.
return
ing 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 usebreak
. You can usebreak
inside anywhile
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 afor
loop as well as awhile
loop. -
A
break
statement only exits the nearest enclosing loop. If you have a nested loop (say, afor
loop inside anotherfor
loop), thebreak
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]