Chapter 6: Flow of Control

In March 1968, the journal Communications of the ACM carried a short letter from the Dutch computer scientist Edsger W. Dijkstra. It ran to barely two pages, and its argument was blunt: the goto statement, which let a program jump from any line to any other line, was doing more harm than good. Programs written that way ran correctly often enough, but nobody — not even the person who wrote them — could look at such a program and say with confidence what would happen next.

Dijkstra was not asking for a small tidy-up. Two years earlier, Corrado Böhm and Giuseppe Jacopini had proved something remarkable: any computation that can be expressed as a program at all can be built out of just three ways of arranging statements. Run them one after another. Choose between them. Repeat them. Nothing else is needed.

That result is why this chapter has the shape it has. You have already written programs in which every statement runs once, top to bottom. Now you will learn the other two arrangements — choosing and repeating — and you will find that a handful of keywords (if, else, elif, for, while, break, continue) is enough to express any decision or any repetition a program will ever need. The difficulty is never the keyword. It is deciding what should be repeated, and when the repeating should stop.

Fig. 6.0

Fig. 6.0 – The three fundamental control structures.

NoteCuriosity Corner

Q1. If a program runs one statement after another from top to bottom, how can it ever take a decision? Q2. Most languages mark the beginning and end of a block with curly brackets. Python uses none. How does it know where a block ends? Q3. Why is it that a for loop always stops on its own, while a while loop can run forever? Q4. What is the difference between walking out of a loop and skipping just one round of it? Q5. Why does asking Python for the range 1 to 10 give you the numbers up to 9 only?

Each of these questions is answered somewhere in this chapter. Watch for them as you read.

6.1 Introduction to Flow of Control

Every program you have written so far has had a single, fixed path through it. The interpreter began at the first line, finished it, moved to the second, and carried on until there was nothing left to run. That path was never in doubt, because you fixed it yourself the moment you decided the order in which to type the statements.

Real tasks are rarely so obedient. A fare table charges one amount for a student and another for an adult. A stock register has to be checked item by item until the last item is reached. A message must be printed only when the quantity in hand falls below a limit. In each of these, what happens next depends on something the program discovers while it is running.

The order in which the statements of a program are executed is called the flow of control. Learning to control that order — to make the program choose and to make it repeat — is what this chapter is about.

NoteDefinition

The flow of control of a program is the order in which its statements are actually executed when the program runs.

Notice the word actually. The order in which statements are written and the order in which they run are two different things, and once this chapter is over they will often differ.

6.1.1 Sequence as the Default Order

When nothing tells Python otherwise, it executes statements one after another, from the top of the program to the bottom, running each exactly once. This arrangement is called sequence, and it is the default flow of control in every program.

Sequence is easy to overlook precisely because it is so plain, but it carries a real guarantee: a statement can safely use a value that an earlier statement produced, because the earlier statement has certainly finished by then. A program that calculates before it reads its input is not a slow program; it is a wrong one.

A straight-line program

Consider a small task at a school stationery counter. A student buys some notebooks, all of the same kind. Given the number of notebooks and the rate of one notebook, print the amount payable.

There is nothing to decide here and nothing to repeat. The method is three steps in a fixed order: read the two inputs, multiply them, print the result. Because the order never varies, sequence alone is enough.

total amount payable at a stationery counter Problem: Read the number of notebooks bought and the rate of one notebook. Print the total amount payable. Input is taken from the keyboard.

# Amount payable for notebooks bought at the school stationery counter
count = int(input("Enter number of notebooks: "))
rate = float(input("Enter rate of one notebook: "))
amount = count * rate
print("Total amount payable is", amount)

Sample output:

Enter number of notebooks: 4
Enter rate of one notebook: 25
Total amount payable is 100.0

Identifier description:

Table 6.1 – identifiers used in the notebook billing program, their type and purpose

Identifier Type Purpose
count int Number of notebooks bought
rate float Price of one notebook
amount float Total payable, obtained as count * rate

Notes: input() always hands back a string, so the two readings are converted before they are used — int() for a count, which is always a whole number, and float() for a rate, which need not be. The printed amount ends in .0 because one of the two operands is a float, and multiplying an int by a float in Python gives a float.

The three working lines of this program can be laid side by side with what the interpreter holds after each one. Reading such a table is a habit worth forming now, because from the next section onwards the order of the rows will stop being obvious.

the notebook billing program, for the input 4 and 25 | Step | Statement executed | count | rate | amount | Printed | |—|—|—|—|—|—| | 1 | count = int(input(...)) | 4 | — | — | prompt only | | 2 | rate = float(input(...)) | 4 | 25.0 | — | prompt only | | 3 | amount = count * rate | 4 | 25.0 | 100.0 | — | | 4 | print(...) | 4 | 25.0 | 100.0 | Total amount payable is 100.0 |

Result: the output is Total amount payable is 100.0. The trace shows that each statement ran once, in the order written, and that amount had no value at all until step 3 gave it one.

Q. What would happen if the third and fourth lines of this program were interchanged?

A. The program would stop with a NameError, because print() would ask for the value of amount before any statement had created it. In sequence, an earlier line may feed a later one, never the other way round.

Where sequence runs out

Now change the task slightly. Suppose notebooks are sold at a lower rate when ten or more are bought. The program can no longer be a straight line, because two different calculations are possible and only one of them should run. Change it again: suppose the counter must print a bill for every student in a queue, and nobody knows in advance how long the queue is. Now a group of statements has to run an unknown number of times.

Neither of these can be written as a sequence, however cleverly the statements are arranged. Sequence can only say do this, then this, then this. It has no way to say do this only if or do this again.

Fig. 6.1

Fig. 6.1 – Sequence flow of control.

6.1.2 Control Structures in Python

To go beyond a straight line, a language must provide constructs that let a program alter its own order of execution. Such a construct is called a control structure. Python supports two types of control structures — selection and repetition.

NoteDefinition

A control structure is a programming construct that determines the flow of control of a program, that is, the order in which its statements are executed.

Selection lets a program choose. It tests a condition and, on the strength of the result, executes one block of statements and skips another. The bill that charges a lower rate for ten or more notebooks needs selection: the condition is count is at least ten, and exactly one of the two calculations must follow from it.

Repetition lets a program repeat. It executes the same block again and again while a condition continues to hold. The queue of students at the counter needs repetition: the same read-calculate-print block runs once per student, and the number of students is not known when the program is written.

Table 6.2 – selection compared with repetition

Feature Selection Repetition
What it decides Whether a block runs at all How many times a block runs
Number of times a chosen block runs Once, at most Zero, one or many times
Effect of the condition being false The block is skipped and control moves on The loop ends and control moves on
Is the condition tested again? No, it is tested once Yes, before or after each round
Typical use Charging a concessional fare to a student Printing a fare receipt for every passenger
Keywords used in Python if, else, elif for, while

The two are not rivals, and a serious program uses both at once — a loop that runs over every item in a stock register, with a test inside it that reports only the items running low. Sections 6.2 and 6.4 take them one at a time; section 6.6 puts them together.

Fig. 6.2

Fig. 6.2 – Selection compared with repetition.

There is one more thing Python needs before either structure can work. Both of them act on a block — a group of statements that is chosen or repeated as a single unit — so the language must have some way of marking where a block begins and ends. Python’s way of doing this is unusual enough to deserve a section of its own, and it is taken up in section 6.3.

Take the notebook billing program above and add one line that prints the number of notebooks bought before it prints the amount. Then move that new line to three different places in the program — before the first input(), between the two input() lines, and at the very end — and run it each time. For which placements does the program still work, and why?

6.2 Selection

Think of the counter at a school library. A book comes back, and the person at the counter looks at the date. If the book has been kept beyond the allowed period, a late entry is made; if not, the book is simply shelved. The same person, the same counter, the same register — but two different actions, and which one happens is decided on the spot, by the date.

A program that must behave this way needs a way of asking a question whose answer is either true or false, and of acting on the answer. In Python that question is written as a condition, and the statements that depend on it are grouped into a block.

NoteDefinition

A condition is an expression that evaluates to either True or False. A block is a group of statements that is treated as a single unit, to be executed or skipped together.

You have met conditions already, in Class XI, as expressions built with the relational operators >, <, >=, <=, == and !=, and joined where necessary with and, or and not. Nothing new is needed here. What is new is that the value of such an expression will now decide which part of your program runs.

6.2.1 The if Statement

The simplest form of selection has only one block, and only one question to ask: should this block run, or should it be skipped? Python writes it with the keyword if.

Return to the library counter. Suppose a book may be kept for fourteen days. The program should note a late return, and in every case record the entry.

days = int(input("Enter number of days the book was kept: "))
if days > 14:
    print("Book returned late")
print("Entry recorded in the register")

For an input of 18, the output is:

Enter number of days the book was kept: 18
Book returned late
Entry recorded in the register

For an input of 9, only the last line appears. The print() that reports the late return was skipped entirely — not run with a different result, but not run at all.

Look closely at the two print() calls. The first is indented and the second is not, and that difference is the whole reason they behave differently. The indented statement belongs to the if; the unindented one does not, so it runs whatever the condition turns out to be. There is no limit on how many statements may sit inside the block, as long as they are all indented alike.

the if statement

if condition:
    statement(s)

Parts: condition is any expression that evaluates to True or False; statement(s) is the block that depends on it, written indented below the header line. Returns / Effect: if the condition is True, the block is executed once and control then moves to the first statement after the block; if it is False, the block is skipped and control moves to that same statement. Example:

marks = 33
if marks < 35:
    print("Needs improvement")

Two details of the header line are easy to lose and hard to do without: the colon at the end, and the indentation of the line below it. Both are compulsory.

Q1. If a program runs one statement after another from top to bottom, how can it ever take a decision?

A. It cannot, as long as it is written in sequence alone. The if statement changes that. It attaches a condition to a block of statements, and the interpreter evaluates that condition while the program is running. If the condition is True the block is executed; if it is False the block is skipped and control jumps straight to the statement after it. So the program still moves forward through the text of the code, but it is now allowed to leave parts of that text unexecuted — and which parts it leaves out depends on the data it was given.

using = where a comparison is meant What students write:

if days = 14:
    print("Due today")

What happens: Python reports a syntax error and refuses to run the program at all. Recent versions add a hint suggesting ==. The fix: write if days == 14:. A single = assigns a value to a name; a double == asks whether two values are equal. A condition must ask, not assign.

6.2.2 The if..else Statement

An if on its own can only skip. Very often, though, the two outcomes are equally real: a jump either qualifies for the final round or it does not, and something must be said in both cases. Writing a second if with the opposite condition would work, but it makes the reader check twice what is really one question.

The if..else statement gives the question two branches. If the condition is True, the first block runs; otherwise the second block runs. Exactly one of the two always runs, never both and never neither.

distance = float(input("Enter the jump distance in metres: "))
if distance >= 4.5:
    print("Qualified for the final round")
else:
    print("Not qualified")

For an input of 4.2, the output is:

Enter the jump distance in metres: 4.2
Not qualified

Note that else takes no condition of its own. It is not asking a second question; it is catching everything the first question left out. That is why it ends with a bare colon.

the if..else statement

if condition:
    statement(s)
else:
    statement(s)

Parts: condition is tested once; the first block runs when it is True, the second when it is False. The else keyword aligns exactly with the if it belongs to. Returns / Effect: exactly one of the two blocks is executed; control then continues from the first statement after the whole if..else. Example:

qty = 0
if qty > 0:
    print("Available")
else:
    print("Out of stock")

Fig. 6.3

Fig. 6.3 – Execution flow of an if..else statement.

Q. If the else part is left out and the condition turns out to be False, what does the program do?

A. Nothing at all for that statement — it skips the block and carries on with whatever follows. An if without else is perfectly legal, and is the right choice when there is genuinely nothing to be done in the other case.

6.2.3 The if..elif..else Chain

Two branches are not always enough. City bus fares, for instance, are usually charged in slabs: one fare up to five kilometres, a higher one up to fifteen, a higher one still up to thirty, and a flat maximum beyond that. Here a single condition cannot sort the passenger into the right slab, because there are four possible answers and only one of them is correct for any journey.

For such cases Python allows the conditions to be chained with elif, which is short for else if. The conditions are tested in the order written. The first one that turns out to be True has its block executed, and the whole chain then ends — the remaining conditions are not even looked at. If none is true, the else block runs.

the if..elif..else chain

if condition:
    statement(s)
elif condition:
    statement(s)
elif condition:
    statement(s)
else:
    statement(s)

Parts: any number of elif branches may follow the if, each with its own condition; the final else has no condition and is optional. Returns / Effect: at most one block in the whole chain is executed — the block belonging to the first condition found to be True, or the else block if none is. Example:

temp = 41
if temp < 20:
    print("Cool")
elif temp < 35:
    print("Warm")
else:
    print("Hot")

Before writing the fare program, it is worth setting out the method, because the ordering of the tests is the whole problem here.

fare for a city bus journey charged in distance slabs Input: the distance of the journey in kilometres, a number greater than zero. Output: the fare in rupees, according to the slabs 10 up to 5 km, 20 up to 15 km, 35 up to 30 km, and 50 beyond 30 km. Method:

1. Read the distance km.
2. If km is not more than 5, set fare to 10.
3. Otherwise, if km is not more than 15, set fare to 20.
4. Otherwise, if km is not more than 30, set fare to 35.
5. Otherwise, set fare to 50.
6. Print the fare.

Why it works: the tests are written from the smallest slab upwards, and a test is reached only when every test above it has failed. So when step 3 is reached, the distance is already known to be more than 5, and the single test km <= 15 is enough to place it in the second slab. Each condition therefore needs to check only its upper limit. Watch out: the last branch has no test of its own. Every distance that gets past the third test belongs to it, so no journey can escape the chain without a fare.

bus fare charged in distance slabs Problem: Read the distance of a city bus journey in kilometres and print the fare. The fare is 10 rupees up to 5 km, 20 rupees up to 15 km, 35 rupees up to 30 km, and 50 rupees for any longer journey.

# Fare for a city bus journey, charged in distance slabs
km = float(input("Enter distance to be travelled in km: "))
if km <= 5:
    fare = 10
elif km <= 15:
    fare = 20
elif km <= 30:
    fare = 35
else:
    fare = 50
print("Fare for", km, "km is", fare, "rupees")

Sample output:

Enter distance to be travelled in km: 12
Fare for 12.0 km is 20 rupees

Identifier description:

Table 6.3 – identifiers used in the bus fare program, their type and purpose

Identifier Type Purpose
km float Distance of the journey, read from the keyboard
fare int Fare in rupees for the slab the distance falls into

Notes: the distance is read as a float because a journey may be 12.5 km, while the fare is a whole number of rupees and stays an int. The printed distance shows as 12.0 rather than 12 for the same reason it did in section 6.1 — float() was applied to it.

the bus fare program, for the input 12 | Step | Test evaluated | Result | fare | Control goes to | |—|—|—|—|—| | 1 | km <= 5 → 12.0 <= 5 | False | — | next elif | | 2 | km <= 15 → 12.0 <= 15 | True | 20 | end of the whole chain | | 3 | not evaluated (km <= 30) | — | 20 | — | | 4 | not evaluated (else) | — | 20 | — | | 5 | print(...) | — | 20 | — |

Result: the output is Fare for 12.0 km is 20 rupees. The trace shows the point of a chain — once a condition succeeds, the rest are not tested at all.

Why not simply write four ifs

A reasonable question at this stage is why the elif keyword is needed, when four separate if statements would also assign a fare. They would, but they would not mean the same thing, and the difference shows up as soon as the conditions overlap.

Table 6.4 – a chain of elif compared with separate if statements

Feature if..elif..else chain Separate if statements
Number of blocks that can run At most one Any number, including all of them
Conditions tested after one succeeds None; the chain ends All of them, every time
Behaviour when conditions overlap The first match wins Every match runs, so a later one overwrites an earlier one
Work done for a value in the first slab One test Four tests
Suits Alternatives that exclude one another Independent checks that may all apply

For the fare slabs the alternatives exclude one another — a journey belongs to exactly one slab — so the chain is the honest way to write it. A stock report that must flag an item as low, as expiring soon and as priced above the limit is the opposite case: those three checks are independent, all three may apply to the same item, and three separate if statements are then correct.

writing the slab conditions in the wrong order What students write:

if km <= 30:
    fare = 35
elif km <= 15:
    fare = 20
elif km <= 5:
    fare = 10
else:
    fare = 50

What happens: no error message, and the program runs quite happily — but a 3 km journey is charged 35 rupees. The first condition is true for it, so the chain stops there and the two narrower slabs below are never reached. The fix: in a chain of overlapping conditions, write the narrowest test first and widen as you go down. Each condition then only has to rule out what the ones above it have already ruled out.

6.2.4 Nested if Statements

The block belonging to an if may contain any statements at all — including another if. An if statement written inside the block of another if statement is called a nested if, and it is how a program asks a follow-up question that only makes sense once the first has been answered.

A stock check at a store makes the point. The first question is whether the item is in stock at all. Only if it is does a second question arise: is the quantity low enough to reorder? Asking about the reorder level for an item that has run out entirely would be pointless, and the nesting says so.

stock check with a reorder warning Problem: Read the name of an item and the quantity in hand. Report that the item is out of stock if the quantity is zero, that it is running low if fewer than 20 units remain, and that stock is sufficient otherwise.

# Stock check at a school stationery store
item = input("Enter item name: ")
qty = int(input("Enter quantity in hand: "))
if qty > 0:
    if qty < 20:
        print(item, "is running low, place an order")
    else:
        print(item, "has sufficient stock")
else:
    print(item, "is out of stock")

Sample output:

Enter item name: Chalk box
Enter quantity in hand: 8
Chalk box is running low, place an order

Identifier description:

Table 6.5 – identifiers used in the stock check program, their type and purpose

Identifier Type Purpose
item str Name of the item being checked
qty int Number of units in hand

Notes: the inner if..else runs only when the outer condition is true, so qty is already known to be positive by the time the reorder level is tested. The outer else needs no test of its own, because the only way to reach it is for qty > 0 to have failed.

The indentation is doing real work here. The inner if and its else are indented one level in from the outer if, and the outer else is aligned with the outer if. Move the outer else in by four more spaces and Python will read it as belonging to the inner if instead, which changes the meaning of the program completely — an item with zero in hand would then produce no message at all.

Fig. 6.4

Fig. 6.4 – Execution flow of a nested if statement.

There is no restriction on how deep the nesting may go, and a chain of elif may sit inside another if just as easily. Depth, though, costs clarity: three or four levels of nesting are hard to read and harder to check. Where a chain of elif will say the same thing flatly, prefer it.

Change the inner condition of the stock check program from qty < 20 to qty <= 20 and run it with a quantity of exactly 20. Which message appears now, and which appeared before? Then predict, without running it, what the program will print for a quantity of 0 in both versions.

6.3 Indentation

In most programming languages, the statements of a block are enclosed in curly brackets. The brackets tell the compiler where the block begins and ends, and the way the code is laid out on the page is left entirely to the programmer’s taste. Neat programmers indent their blocks; careless ones do not; the language does not care either way.

Python took a different decision. It has no brackets around a block. Instead, the leading blank space at the beginning of a line — spaces or tabs — is itself part of the syntax. This leading whitespace is called indentation, and statements written with the same indentation belong to the same block.

NoteDefinition

Indentation is the leading whitespace, that is, the spaces or tabs, at the beginning of a statement. In Python, statements written at the same level of indentation form a single block of code.

The consequence is worth stating plainly: in Python, the layout of a program is not decoration. Change the indentation of a line and you have changed the program.

Where a block begins and ends

A header line that introduces a block — an if, an elif, an else, and later a for or a while — always ends in a colon. The block belonging to it is everything written below it at a deeper level of indentation. The block ends at the first line that returns to the header’s own level, and that line is no longer part of it.

Nothing else marks the boundary. There is no closing bracket, no end keyword, nothing at the bottom of the block at all — the return to the earlier level is the only signal, which is why an accidental space matters so much.

Fig. 6.5

Fig. 6.5 – Defining blocks through indentation.

Q2. Most languages mark the beginning and end of a block with curly brackets. Python uses none. How does it know where a block ends?

A. It reads the indentation. A block begins on the line after a header ending in a colon, and consists of every following line indented further than that header. The block ends the moment a line appears at the header’s own level of indentation — that line belongs to the enclosing code, not to the block. So the boundary is not marked by any symbol you type at the end; it is marked by the point at which you stop indenting. The interpreter checks these levels strictly, and reports an error if they do not fit together.

attendance check for eligibility Problem: A term has 40 working days. Read the number of days a student was present and print the two eligibility messages only if attendance is at least 75 per cent. In every case, print a closing line saying the report has been generated.

# Attendance check for a term of 40 working days
present = int(input("Enter number of days present: "))
total = 40
percent = present * 100 / total
if percent >= 75:
    print("Attendance is sufficient")
    print("Eligible to appear")
print("Report generated for", present, "days")

Sample output:

Enter number of days present: 28
Report generated for 28 days

Identifier description:

Table 6.6 – identifiers used in the attendance check program, their type and purpose

Identifier Type Purpose
present int Number of days the student attended
total int Number of working days in the term, fixed at 40
percent float Attendance percentage, computed as present * 100 / total

Notes: percent is a float because the / operator in Python 3 always gives a float, even when the division comes out exact. Two statements sit inside the if block and one sits outside it, and the only thing distinguishing them is four spaces.

Run the same program with 34 days present and all three lines appear, because 85.0 clears the limit. Run it with 28 and only the last line appears. The two indented print() calls are a single block, chosen or skipped together; the third is not part of the block at all.

pulling a statement out of the block by mistake What students write:

if percent >= 75:
    print("Attendance is sufficient")
print("Eligible to appear")

What happens: no error message is produced, and the program runs. But for a student present on 28 days, it prints Eligible to appear — a statement that is now outside the if and therefore runs whatever the attendance may be. The fix: indent the second print() to the same level as the first, so that both belong to the block. When one statement of a group is at a different level from the rest, Python does not guess your intention; it obeys the indentation exactly as written.

When the interpreter refuses

The mis-indentation above was legal Python that meant the wrong thing. Some indentation mistakes are not legal at all, and those the interpreter catches for you.

If a header line ending in a colon is not followed by an indented line, there is no block for it to control, and Python reports an IndentationError saying that an indented block was expected. If a line is indented for no reason — no header above it asking for a block — the error reports an unexpected indent instead.

count = 5
    print(count)

The second line here is indented although nothing invited it to be, and the program stops before running with an IndentationError.

A third mistake is harder to see, because the page looks correct. If some lines of a block are indented with tab characters and others with spaces, the two may line up on screen and still not match as far as the interpreter is concerned. Python reports this as a TabError, saying that tabs and spaces have been used inconsistently. The cure is simple and worth adopting from the start: pick one, use it everywhere, and let the editor insert it.

Q. Does the block have to be indented by exactly four spaces?

A. No. Any consistent amount works, as long as every statement of the same block is indented alike. Four spaces is the convention followed almost universally in Python, and this book uses four throughout, one level per depth of nesting.

Indentation is often described as Python’s most controversial design decision, and beginners meeting it after a language with brackets do resent it at first. But it removes a whole category of bug that other languages live with — the program whose layout says one thing and whose brackets say another. In Python, what the block looks like is what the block is.

Key in the attendance check program and run it with 30 days present. Then indent the last print() by four spaces so that it joins the block, and run it again with the same input. Write down the output in both cases and explain, in one sentence, why they differ.

6.4 Repetition

An electricity bill is paid every month. A butterfly lays eggs, the eggs become caterpillars, the caterpillars become pupae, the pupae become butterflies, and the cycle begins again. A bus runs the same route, stop after stop, twice a day for years. Repetition is so ordinary in the world that we rarely notice how much of what we do is the same action done again on new material.

Programs are no different, and the repetition in them is called iteration. The construct that carries it out is a loop — a block of statements that the interpreter is willing to execute more than once.

NoteDefinition

Iteration is the repeated execution of a set of statements in a program. A loop is a construct that carries out iteration, executing its block of statements again and again as long as a stated condition holds.

Python provides two looping constructs: for and while. They differ in how they decide when to stop, and choosing correctly between them is most of what this section is about.

6.4.1 From Repeated Statements to a Loop

Suppose a class has five subjects and you want to print a heading line for each, so that marks can be written under it. Without a loop, the program is five statements that differ only in a name:

print("Marks entered for English")
print("Marks entered for Physics")
print("Marks entered for Chemistry")
print("Marks entered for Mathematics")
print("Marks entered for Computer Science")

This works. It is also the last size at which it works. Ask for the same heading for every one of 500 students in the school and no one is going to type 500 statements, and no one who did would be able to check them. Worse, the program is stuck at the size it was written for: add a subject, and the program has to be edited.

What is really wanted is one statement and a rule for how often to run it. Set out in words, the method for the marks headings is:

1. Take the first subject in the list.
2. Print the heading for it.
3. Move to the next subject.
4. Repeat steps 2 and 3 until there are no subjects left.

The interesting part of that method is step 4. Something has to decide when the repetition stops. Here the answer is when the list runs out, and the number of headings is therefore fixed the moment the list is fixed. In other problems the answer is when some running total is large enough, or when the user says so, and the number of rounds is not known in advance at all. That difference is exactly the difference between Python’s two loops.

6.4.2 The for Loop

The for statement is used to iterate over the items of a sequence — the elements of a list or a tuple, the characters of a string, or a range of numbers. It takes the items one at a time, in order, and runs its block once for each. When the items are exhausted, the loop ends and control passes to the statement after it.

The five headings above become three lines:

subjects = ["English", "Physics", "Chemistry"]
for name in subjects:
    print("Marks entered for", name)
Marks entered for English
Marks entered for Physics
Marks entered for Chemistry

The name name here is the loop control variable. You do not assign anything to it yourself; the for statement assigns it, before each round, the next item of the sequence. In the first round it holds "English", in the second "Physics", in the third "Chemistry" — and then there is nothing left to assign, so the loop stops.

A string is a sequence too, and a for loop walks it one character at a time:

for ch in "PIN":
    print(ch, end="-")
P-I-N-

The end parameter of print() says what to write after the value instead of moving to a new line, so here the three characters appear on one line, each followed by a hyphen — including the last, which is why the output ends with a hyphen too.

Fig. 6.6

Fig. 6.6 – Execution flow of a for loop.

Q. What does a for loop do if the list it is given is empty?

A. Nothing. There is no first item to assign, so the body is not executed even once and control moves straight past the loop. This is normal behaviour, not an error — a program that totals a list of readings should quite properly report a total of zero when there are no readings.

6.4.2.1 Syntax of the for Loop

the for loop

for control_variable in sequence:
    statement(s)

Parts: control_variable is the name that receives each item in turn; sequence is any object whose items can be taken one after another — a list, a tuple, a string, or a range; statement(s) is the body of the loop, indented below the header. Returns / Effect: the body is executed once for every item of the sequence, with the control variable holding that item; when the items are exhausted, control passes to the first statement after the body. Example:

for reading in [4, 9, 16]:
    print(reading)

As with if, the header ends in a colon and the body is marked out by its indentation, exactly as section 6.3 described.

assigning to the control variable inside the loop What students write:

for num in [3, 5, 7]:
    num = num * 10
    print(num)

What happens: the program prints 30, 50 and 70, which looks right — but the list is untouched, and printing [3, 5, 7] afterwards proves it. The assignment changed only the variable, not the item it was copied from. The fix: if the loop is meant to report changed values, this code is correct as it stands. If it is meant to change the list itself, the loop must write back into the list, which needs the index positions covered later in the chapter on lists. The point to hold on to is that the control variable is refreshed at the top of every round, so anything you assign to it is discarded there.

6.4.2.2 The range() Function

Very often the items to be iterated over are not stored anywhere; they are simply the numbers 1 to 10, or 0 to 99, or 5 down to 1. Writing them out as a list would defeat the purpose. For these, Python supplies the built-in function range(), which produces a sequence of integers from a start value up to — but not including — a stop value, in steps of a given size.

range() does not build a list of those numbers in memory. It returns a range object, which hands over the next integer whenever one is asked for. That is exactly what a for loop needs, and it costs the same whether the range covers ten numbers or ten crore. To see the numbers, wrap the range in list().

>>> list(range(6))
[0, 1, 2, 3, 4, 5]
>>> list(range(3, 9))
[3, 4, 5, 6, 7, 8]
>>> list(range(4, 25, 6))
[4, 10, 16, 22]
>>> list(range(10, 0, -2))
[10, 8, 6, 4, 2]

Read those four lines carefully, because they contain everything range() does. With one argument, counting starts at 0 and steps by 1. With two, the first is the start and the second the stop. With three, the last is the step, which may be negative to count downwards. All the arguments must be integers, and the step may not be zero — a step of zero would never move towards the stop value, and Python refuses it with an error rather than running forever.

the range() function

range(start, stop, step)

Parts: stop is compulsory and is not included in the sequence; start is optional and defaults to 0; step is optional, defaults to 1, may be negative, and may not be zero. Returns / Effect: a range object that produces the integers start, start + step, start + 2 * step … continuing while they remain on the near side of stop. Example:

for seat in range(1, 5):
    print("Seat number", seat)

Q5. Why does asking Python for the range 1 to 10 give you the numbers up to 9 only?

A. Because in range() the stop value marks the point at which counting stops, not the last value produced. range(1, 10) therefore gives 1 to 9. The convention looks odd at first, but it buys something useful: the count of numbers produced is simply stop minus start, so range(0, 10) is exactly ten numbers and range(1, 10) is exactly nine. It also means two ranges written back to back, such as range(0, 5) and range(5, 10), fit together with no gap and no overlap. If you want the value 10 included, ask for range(1, 11).

A loop that reads a list

The two ideas so far — a for loop and a condition inside it — combine into something genuinely useful: a loop that visits every item of a collection and treats some of them differently.

total rainfall and count of heavy-rain days Input: a list of daily rainfall readings in millimetres. Output: the total rainfall over those days, and the number of days on which the reading exceeded 50 mm. Method:

1. Set total to 0 and heavy to 0.
2. Take the next reading from the list.
3. Add the reading to total.
4. If the reading is greater than 50, add 1 to heavy.
5. Repeat steps 2 to 4 until every reading has been taken.
6. Print total and heavy.

Why it works: both answers are built up one reading at a time, and every reading is visited exactly once, so nothing is counted twice and nothing is missed. Starting total and heavy at 0 is what makes the very first addition correct. Watch out: the two counters must be set to 0 before the loop, not inside it. Setting them inside would reset them on every round, and the program would end up reporting only the last reading.

total rainfall and heavy-rain days from a week’s readings Problem: Given the rainfall recorded on five days, in millimetres, print the total rainfall for the period and the number of days on which more than 50 mm was recorded. The readings are fixed in the program.

# Rainfall recorded on five days, in millimetres
rainfall = [12, 68, 0, 45, 91]
total = 0
heavy = 0
for reading in rainfall:
    total = total + reading
    if reading > 50:
        heavy = heavy + 1
print("Total rainfall:", total, "mm")
print("Days of heavy rain:", heavy)

Sample output:

Total rainfall: 216 mm
Days of heavy rain: 2

Identifier description:

Table 6.7 – identifiers used in the rainfall program, their type and purpose

Identifier Type Purpose
rainfall list The five daily readings in millimetres
reading int Loop control variable, holding one day’s reading at a time
total int Running total of the readings seen so far
heavy int Running count of readings above 50 mm

Notes: total and heavy are both accumulators — variables that carry a partial answer from one round of the loop into the next. The if inside the loop is indented one level deeper than the loop body’s first statement, so it is executed on every round, while the statement it controls is not.

the rainfall program, on the readings 12, 68, 0, 45, 91 | Round | reading | total after step | reading > 50 | heavy after step | |—|—|—|—|—| | before loop | — | 0 | — | 0 | | 1 | 12 | 12 | False | 0 | | 2 | 68 | 80 | True | 1 | | 3 | 0 | 80 | False | 1 | | 4 | 45 | 125 | False | 1 | | 5 | 91 | 216 | True | 2 |

Result: the output is Total rainfall: 216 mm followed by Days of heavy rain: 2. The trace shows the two accumulators growing independently — total on every round, heavy only on the rounds where the condition held.

6.4.3 The while Loop

The for loop is the right tool when the items are known in advance. Many problems are not like that. A student putting aside ₹500 a week towards an excursion fee wants to know how many weeks it will take to reach ₹2500 — and the number of weeks is the answer, not something that can be written into the loop beforehand.

For such problems Python provides the while loop. It has no sequence to walk through. Instead it tests a condition before each round: while the condition is True, the body runs again; the moment it is False, the loop ends. If the condition is false the very first time it is tested, the body does not run even once.

the while loop

while test_condition:
    statement(s)

Parts: test_condition is any expression evaluating to True or False, tested before every round including the first; statement(s) is the body of the loop. Returns / Effect: the body is executed repeatedly for as long as the condition remains True; when it becomes False, control passes to the first statement after the body. Example:

count = 3
while count > 0:
    print(count)
    count = count - 1

Fig. 6.7

Fig. 6.7 – Execution flow of a while loop.

weeks needed to save a fixed amount Problem: A student saves ₹500 at the end of every week towards an excursion fee of ₹2500. Print the savings at the end of each week, and finally the number of weeks needed to reach the fee.

# Weekly savings towards an excursion fee
fee = 2500
weekly = 500
savings = 0
weeks = 0
while savings < fee:
    savings = savings + weekly
    weeks = weeks + 1
    print("End of week", weeks, ": savings are", savings)
print("The fee is covered in", weeks, "weeks")

Sample output:

End of week 1 : savings are 500
End of week 2 : savings are 1000
End of week 3 : savings are 1500
End of week 4 : savings are 2000
End of week 5 : savings are 2500
The fee is covered in 5 weeks

Identifier description:

Table 6.8 – identifiers used in the savings program, their type and purpose

Identifier Type Purpose
fee int Amount to be reached, in rupees
weekly int Amount saved at the end of each week
savings int Total saved so far
weeks int Count of weeks completed

Notes: the space before the colon in the printed lines comes from print(), which puts a space between the values it is given; weeks and ":" are two such values. Nothing in this program says how many rounds to run — the count of weeks emerges from the loop rather than controlling it.

the savings program | Round | savings < 2500 tested as | Result | savings | weeks | Printed | |—|—|—|—|—|—| | 1 | 0 < 2500 | True | 500 | 1 | End of week 1 : savings are 500 | | 2 | 500 < 2500 | True | 1000 | 2 | End of week 2 : savings are 1000 | | 3 | 1000 < 2500 | True | 1500 | 3 | End of week 3 : savings are 1500 | | 4 | 1500 < 2500 | True | 2000 | 4 | End of week 4 : savings are 2000 | | 5 | 2000 < 2500 | True | 2500 | 5 | End of week 5 : savings are 2500 | | 6 | 2500 < 2500 | False | 2500 | 5 | loop ends |

Result: the loop runs five times and the final line reports 5 weeks. The trace shows the condition being tested six times for five rounds — the last test is the one that ends the loop.

Choosing between for and while

Both loops repeat a block, and many problems can be forced into either. The question to ask is not which one you know better, but whether the number of rounds is settled before the loop starts.

Table 6.9 – the for loop compared with the while loop

Feature for loop while loop
What it repeats over The items of a sequence or range Any condition that stays True
Number of rounds Known before the loop begins Not known; discovered as the loop runs
Control variable Set automatically to the next item each round Created and updated by the programmer
Where the update happens Built into the statement Must be written inside the body
If there is nothing to do Empty sequence, body runs zero times Condition false at the start, body runs zero times
Risk of never stopping Practically none, the sequence is finite Real, if the body never makes the condition false
Suits Visiting every mark in a list, counting 1 to 50 Saving until a target is met, reading until the user stops

6.4.4 Infinite Loops and the Exit Condition

A while loop stops only when its condition becomes false, and nothing in the language guarantees that it ever will. If the body does not change anything the condition depends on, the condition will be just as true on the thousandth test as it was on the first. Such a loop is called an infinite loop, and it does not end until the program is stopped from outside.

NoteDefinition

An infinite loop is a loop whose condition never becomes false, so that the loop never terminates on its own.

The commonest cause is a forgotten update.

forgetting to update the variable the condition tests What students write:

count = 1
while count <= 5:
    print("Round", count)

What happens: count stays at 1 for ever, so count <= 5 is always True and the program prints Round 1 endlessly until it is interrupted from the keyboard. The fix: add count = count + 1 as the last statement of the body, indented to the same level as the print(). Every while loop needs three things — a starting value, a condition, and something in the body that moves the value towards making that condition false.

Those three things are worth naming, because checking for them is a reliable way to know whether a loop you have written will stop. The starting value is set before the loop. The condition is the test in the header. The update happens inside the body, and it must move in the right direction — a loop that counts down while testing count <= 5 will run away just as surely as one that never counts at all.

Fig. 6.8

Fig. 6.8 – The three components of a while loop.

Not every infinite loop is a mistake. A program running a display board or waiting for input may be written deliberately to loop for ever, and section 6.5 introduces the break statement, which gives such a loop a way out from the inside. What must never happen by accident is a loop that was meant to stop and cannot.

Q3. Why is it that a for loop always stops on its own, while a while loop can run forever?

A. Because the two loops decide differently when to stop. A for loop is given a sequence — a list, a string, a range — and it stops when the items run out. The sequence is finite and the loop takes each item once, so the number of rounds is fixed before the first round begins and the loop cannot help ending. A while loop is given only a condition, and it keeps going as long as that condition is True. Whether the condition ever becomes false depends entirely on what the body of the loop does. If the body changes nothing the condition depends on, the loop runs for ever — and Python will not warn you, because an endlessly repeating loop is perfectly legal code.

Q. Can a while loop’s body fail to run at all?

A. Yes, and that is often correct. The condition is tested before the first round, so a student who has already saved ₹2500 enters the loop with the condition false and saves nothing further. A loop that runs zero times is doing exactly what it was asked to.

Practical link: this task exercises the while loop and the accumulator pattern of section 6.4, and is filed under Python programs in the practical report file. Write a program that reads whole numbers one at a time from the keyboard and keeps a running total, stopping when the user enters 0, and then prints how many numbers were added and what their total and average were. The average must be printed only when at least one number was entered. Test it with the inputs 20, 35, 45, 0 — and then test it again by entering 0 as the very first input, and make sure the program does not fail.

6.5 Break and Continue Statements

A loop, as section 6.4 left it, is an all-or-nothing arrangement. A for loop visits every item of its sequence; a while loop runs until its condition fails. Neither has any way of reacting to something discovered halfway through.

Yet that is often exactly what is needed. A librarian searching an issue register for one accession number stops the moment it is found — reading the remaining pages would tell her nothing she does not already know. A teacher totalling a set of answer scripts skips the ones marked absent and carries on with the next, without abandoning the totalling.

The two situations are different, and Python gives them two different statements. break leaves the loop altogether. continue abandons only the current round and goes back for the next one.

6.5.1 The break Statement

The break statement terminates the loop in which it occurs. Execution does not go back to the top, and the remaining items or rounds are not processed at all; control passes straight to the first statement after the loop body.

NoteDefinition

The break statement immediately terminates the loop containing it, and transfers control to the statement following that loop.

Searching is the natural setting for it. Suppose a list holds the accession numbers of the books currently issued from a school library, and you want to know whether a particular book is among them.

searching a list for a given value Input: a list of accession numbers, and the number to be searched for. Output: a message saying whether the number is present in the list. Method:

1. Set found to False.
2. Take the next number from the list.
3. If it equals the number searched for, set found to True and leave the loop.
4. Repeat steps 2 and 3 until every number has been examined.
5. If found is True, report that the book is issued; otherwise report that it is not.

Why it works: the loop can end in only two ways. Either step 3 found a match and left early, in which case found is True; or the list was exhausted without a match, in which case found is still False. The variable therefore records which of the two happened. Watch out: found must be set to False before the loop begins. Without that, the test in step 5 would be reading a variable that does not exist at all when the list is empty.

searching the issued-books list for an accession number Problem: A list holds the accession numbers of books currently issued. Read an accession number from the keyboard and report whether that book is issued. The search must stop as soon as the number is found.

# Accession numbers of the books currently issued
issued = [1042, 1187, 1203, 1315, 1408]
target = int(input("Enter accession number to search: "))
found = False
for number in issued:
    if number == target:
        found = True
        break
if found:
    print("Accession number", target, "is issued")
else:
    print("Accession number", target, "is not in the issued list")

Sample output:

Enter accession number to search: 1203
Accession number 1203 is issued

Identifier description:

Table 6.10 – identifiers used in the search program, their type and purpose

Identifier Type Purpose
issued list Accession numbers of the books currently issued
target int The accession number being searched for
number int Loop control variable, holding one list item at a time
found bool Records whether the target was found

Notes: the if..else at the end is outside the loop, at the same level as the for, so it runs once after the search is over rather than once per item. Reporting from inside the loop would print a message for every number examined.

the search program, for the target 1203 | Round | number | number == target | Action taken | found | |—|—|—|—|—| | before loop | — | — | — | False | | 1 | 1042 | False | next round | False | | 2 | 1187 | False | next round | False | | 3 | 1203 | True | set found, then break | True | | — | 1315, 1408 | not tested | loop already left | True |

Result: the output is Accession number 1203 is issued. The trace shows the saving that break buys — two of the five numbers were never examined, because by then the answer was already known.

That saving is the point. On a list of five it is trivial; on an issue register of fifty thousand entries, a search that stops at the match does a fraction of the work of one that ploughs on to the end. And the result is identical either way, which is what makes the shortcut safe.

Fig. 6.9

Fig. 6.9 – Effect of the break statement.

break works the same way in a while loop, and there it has a second use. A loop written as while True has a condition that can never fail, so on its own it would run for ever; a break inside the body gives it a controlled way out, taken when the program decides the work is done rather than when a counter runs out. That is a legitimate design, and quite different from the accidental infinite loop of section 6.4.4 — here the exit exists, it is simply written inside the body instead of in the header.

6.5.2 The continue Statement

Sometimes the round is unwanted but the loop is not. The continue statement skips the rest of the loop body for the current round and goes back to the top of the loop for the next one. The loop itself is untouched: for a for loop, the next item is taken as usual; for a while loop, the condition is tested again and the loop carries on if it still holds.

NoteDefinition

The continue statement skips the remaining statements in the body of the loop for the current iteration, and transfers control to the beginning of the loop for the next iteration.

Consider a set of answer scripts in which an absent candidate has been recorded as -1 instead of a mark. Those entries must not be added into the total, and they must not be counted as papers evaluated — but the remaining scripts must still be processed.

totalling marks while skipping absent candidates Problem: A list holds the marks of a class, with -1 recorded for every candidate who was absent. Print how many papers were actually evaluated and what the total of their marks is.

# Marks of a class, with -1 recorded for an absent candidate
marks = [56, -1, 78, 91, -1, 64]
total = 0
count = 0
for m in marks:
    if m == -1:
        continue
    total = total + m
    count = count + 1
print("Papers evaluated:", count)
print("Total marks:", total)

Sample output:

Papers evaluated: 4
Total marks: 289

Identifier description:

Table 6.11 – identifiers used in the marks totalling program, their type and purpose

Identifier Type Purpose
marks list Marks of the class, with -1 for an absent candidate
m int Loop control variable, holding one entry at a time
total int Running total of the marks actually awarded
count int Number of papers evaluated

Notes: the two statements after the if are the ones being skipped, so both the addition and the count are missed on an absent entry — which is exactly right, since an absent candidate must not raise either figure. The same result could be had by putting those two statements inside an if m != -1: block, and for a body this short that version reads just as well.

the marks totalling program | Round | m | m == -1 | Rest of the body | total | count | |—|—|—|—|—|—| | before loop | — | — | — | 0 | 0 | | 1 | 56 | False | executed | 56 | 1 | | 2 | -1 | True | skipped | 56 | 1 | | 3 | 78 | False | executed | 134 | 2 | | 4 | 91 | False | executed | 225 | 3 | | 5 | -1 | True | skipped | 225 | 3 | | 6 | 64 | False | executed | 289 | 4 |

Result: the output is Papers evaluated: 4 followed by Total marks: 289. The trace shows the loop completing all six rounds — continue cost two rounds their work, but not their existence.

Table 6.12 – the break statement compared with the continue statement

Feature break continue
Effect on the current round Abandons it Abandons it
Effect on the loop Ends the loop entirely Loop carries on with the next round
Rounds still to come None; they are skipped All of them run as usual
Where control goes next First statement after the loop Top of the loop, for the next round
In a for loop Remaining items are never taken The next item is taken normally
In a while loop The condition is not tested again The condition is tested again
Typical use Stopping a search at the first match Skipping an entry that should not be processed

Q4. What is the difference between walking out of a loop and skipping just one round of it?

A. Walking out is break. It ends the loop there and then — no further items are taken, no further tests are made, and control moves to the statement after the loop. You use it when carrying on could not change the answer, as in a search that has already found what it was looking for. Skipping a round is continue. It abandons only the rest of the body for the current item and goes straight back to the top for the next one, leaving the loop itself perfectly intact. You use it when one particular item does not deserve the treatment the others get, as with an absent candidate in a list of marks. In short, break decides the loop is over; continue decides only that this round is over.

a continue that jumps over the update in a while loop What students write:

i = 0
while i < 6:
    if i == 3:
        continue
    print(i)
    i = i + 1

What happens: the numbers 0, 1 and 2 are printed and then the program hangs. When i becomes 3, continue skips the rest of the body — including the line that increases i — so the condition is tested again with i still 3, and the same thing happens for ever. The fix: in a while loop, update the control variable before the continue, or use an if..else so that the update is never skipped. A for loop does not have this trap, because the control variable is refreshed by the for statement itself.

Fig. 6.10

Fig. 6.10 – Effect of the continue statement.

One restriction applies to both statements, and it matters as soon as loops are put inside loops: break and continue act only on the loop that immediately encloses them. A break written inside an inner loop ends that inner loop, and the outer loop carries on with its next round exactly as though the inner loop had finished normally. Section 6.6 takes up such arrangements.

Take the marks totalling program and replace continue with break, changing nothing else. Predict the two numbers it will print before you run it, then run it and check. Explain in one sentence why the answer is not simply “one fewer paper”.

6.6 Nested Loops

The body of a loop may contain any statements at all, and that includes another loop. A loop written inside the body of another loop is called a nested loop. The one on the outside is the outer loop; the one written inside its body is the inner loop.

NoteDefinition

A nested loop is a loop contained within the body of another loop. The inner loop completes all its rounds for each single round of the outer loop.

The reason such an arrangement is needed is that a great many things come in two dimensions. A bus has rows, and each row has seats. A week has days, and each day has periods. A pattern on the page has lines, and each line has characters on it. In every case the description contains the phrase for each … for each, and that phrase is a nested loop in words.

How the rounds interleave

Take the seat labels of a small bus with three rows and two seats in each row.

for row in range(1, 4):
    for seat in "AB":
        print(row, seat)
1 A
1 B
2 A
2 B
3 A
3 B

Read the output down the page and the pattern of control is plain. The outer loop set row to 1 and then handed over to the inner loop, which ran to completion — both its rounds — before the outer loop was allowed to move on. Then row became 2 and the inner loop started again from the beginning, with seat back at "A".

That restarting is the part worth fixing in your mind. The inner loop does not carry on from where it stopped; it begins afresh on every round of the outer loop. Here the inner loop ran three separate times, of two rounds each, so its body was executed six times in all. In general, an inner loop of \(n\) rounds inside an outer loop of \(m\) rounds executes its body \(m \times n\) times, where \(m\) is the number of rounds of the outer loop and \(n\) the number of rounds of the inner loop.

the seat-label nested loop | Outer round | row | Inner round | seat | Printed | |—|—|—|—|—| | 1 | 1 | 1 | A | 1 A | | 1 | 1 | 2 | B | 1 B | | 2 | 2 | 1 | A | 2 A | | 2 | 2 | 2 | B | 2 B | | 3 | 3 | 1 | A | 3 A | | 3 | 3 | 2 | B | 3 B |

Result: six lines are printed. The trace shows seat returning to A at the start of every outer round, while row changes only once per pair of lines.

Fig. 6.11

Fig. 6.11 – Execution order in a nested loop.

Q. If the outer loop runs 5 times and the inner loop 4 times, how many times does the innermost statement run?

A. Twenty. The inner loop starts afresh on each of the five outer rounds and does its four rounds each time. This multiplying is why deep nesting gets expensive quickly — three loops of a hundred rounds each mean a million executions of the innermost statement.

When the inner loop depends on the outer

So far the inner loop has done the same number of rounds every time. It need not. The inner loop’s range may be written in terms of the outer loop’s control variable, and then each outer round gets an inner loop of a different length. This is what makes patterns possible.

printing a left-aligned triangle of stars Input: the number of rows to be printed, a whole number greater than zero. Output: that many lines, with one star on the first line, two on the second, and so on. Method:

1. Read the number of rows.
2. Let i take the values 1, 2, 3 ... up to the number of rows.
3. For the current i, print a star i times, all on the same line.
4. End the line.
5. Repeat steps 3 and 4 for every value of i.

Why it works: the line number and the number of stars on that line are the same quantity, so the outer control variable can serve as the count for the inner loop. Step 4 is what turns a single long row of stars into a triangle. Watch out: step 4 belongs to the outer loop, not the inner one. Ending the line inside the inner loop would put one star on each line and produce a column, not a triangle.

printing a triangle of stars Problem: Read the number of rows from the keyboard and print a left-aligned triangle of stars, with one star in the first row, two in the second, and so on down to the last row.

# A left-aligned triangle of stars
rows = int(input("Enter number of rows: "))
for i in range(1, rows + 1):
    for j in range(i):
        print("*", end="")
    print()

Sample output:

Enter number of rows: 4
*
**
***
****

Identifier description:

Table 6.13 – identifiers used in the star triangle program, their type and purpose

Identifier Type Purpose
rows int Number of rows in the triangle, read from the keyboard
i int Outer loop control variable; also the number of stars in the current row
j int Inner loop control variable, counting the stars printed in one row

Notes: print("*", end="") keeps the cursor on the same line, so the inner loop lays the stars side by side. The bare print() at the end of the outer body prints nothing but moves to a new line. The value of j is never used inside the inner body — it is there only to make the loop run the right number of times, which is why range(i) is enough.

Fig. 6.12

Fig. 6.12 – Inner loop depending on the outer loop.

putting the line break inside the inner loop What students write:

for i in range(1, 4):
    for j in range(i):
        print("*")

What happens: no error, but the output is a single column of six stars instead of a triangle of three rows. Each print() call ends its own line, so no two stars ever share one. The fix: print with end="" inside the inner loop, and put a bare print() in the outer loop’s body, after the inner loop, indented to the same level as the inner for. The rule to remember is that anything which must happen once per row belongs to the outer loop.

Combining and interrupting nested loops

Python places no restriction on how many loops may be nested or how deep the nesting goes, and the two kinds may be mixed freely — a while inside a for, a for inside a while, or either inside itself. The choice at each level is made on the same ground as before: a for where the number of rounds is settled in advance, a while where it is not.

Section 6.5 noted that break and continue act only on the loop immediately enclosing them. Nesting is where that matters.

for i in range(1, 4):
    for j in range(1, 4):
        if j == 2:
            break
        print(i, j)
1 1
2 1
3 1

The break ends the inner loop each time j reaches 2, so only one line is printed per outer round. But it does not touch the outer loop at all — that carries on to its next value of i exactly as though the inner loop had run out of items in the ordinary way. To leave both loops, a program must either arrange for the outer loop’s own condition to fail as well, or be restructured so that only one loop is needed.

Change the star triangle program so that the triangle is upside down — four stars on the first row, then three, then two, then one. Only the inner loop’s range() needs rewriting, in terms of i and rows. Work out the counts on paper for rows = 4 before you write the code, then run it and check that the last row has exactly one star.

6.7 Summary

  1. The flow of control of a program is the order in which its statements are actually executed. That order is not always the order in which they are written.

  2. When nothing directs otherwise, statements execute in sequence — one after another, from top to bottom, each exactly once. Sequence guarantees that a statement may safely use a value produced by an earlier one.

  3. Python supports two types of control structures: selection and repetition. Selection decides whether a block runs at all; repetition decides how many times it runs.

  4. A condition is an expression evaluating to True or False. A block is a group of statements executed or skipped together as one unit.

  5. The if statement executes its block when the condition is true and skips it otherwise. The if..else statement gives the condition two branches, exactly one of which runs on any execution.

  6. Conditions may be chained with elif. The conditions are tested in the order written; the first true one has its block executed and the rest are not tested at all. The optional else at the end catches everything that no condition matched. Because a chain stops at the first match, overlapping conditions must be written narrowest first.

  7. An if statement written inside the block of another if statement is a nested if. It is used for a follow-up question that arises only after the first has been answered.

  8. Python has no brackets around a block. Indentation — the leading spaces or tabs at the start of a statement — is part of the syntax. Statements at the same level of indentation form one block, and a block ends at the first line that returns to the level of its header. Changing the indentation of a line changes the program.

  9. A header line that introduces a block always ends in a colon. Missing or inconsistent indentation is reported as an IndentationError, and mixing tabs with spaces as a TabError.

  10. Iteration is the repeated execution of a set of statements, carried out by a loop. Python provides two looping constructs, for and while, which differ in how they decide when to stop.

  11. The for loop iterates over the items of a sequence — a list, a tuple, a string or a range — assigning each item in turn to the loop control variable and running the body once for each. It ends when the items are exhausted, so the number of rounds is fixed before the loop begins. An empty sequence means the body runs zero times.

  12. The built-in function range() produces a sequence of integers from a start value up to, but not including, a stop value, in steps of a given size. The start defaults to 0 and the step to 1; the step may be negative but may not be zero, and all arguments must be integers. It returns a range object rather than a list, so list() is needed to display the values.

  13. The while loop tests its condition before every round, including the first, and repeats its body for as long as the condition holds. The number of rounds is not known in advance. If the condition is false at the first test, the body never runs.

  14. Every while loop needs three things: a starting value set before the loop, a condition in the header, and an update inside the body that moves towards making the condition false. A loop whose condition never becomes false is an infinite loop and does not terminate on its own.

  15. The break statement immediately terminates the loop containing it and transfers control to the statement following that loop. Its usual purpose is to stop a search once the answer is known.

  16. The continue statement abandons only the remaining statements of the current round and returns control to the top of the loop for the next one. The loop itself carries on. In a while loop it must not be allowed to skip the update, or the loop will never end.

  17. A loop contained within another loop is a nested loop. The inner loop completes all its rounds afresh for every single round of the outer loop, so an inner loop of n rounds inside an outer loop of m rounds executes its body m times n times in all.

  18. The inner loop’s range may be written in terms of the outer loop’s control variable, giving each outer round an inner loop of a different length. Any type of loop may be nested inside any other, to any depth.

  19. Both break and continue act only on the loop that immediately encloses them. A break inside an inner loop ends that inner loop only; the outer loop continues with its next round.

6.8 Key Terms

  1. ACM — Association for Computing Machinery, the professional body whose journal carried the 1968 letter mentioned at the start of this chapter.

  2. Block — a group of statements treated as a single unit, executed or skipped together; in Python it is marked out by its indentation.

  3. break statement — a statement that immediately terminates the loop containing it and passes control to the statement after that loop.

  4. Condition — an expression that evaluates to either True or False, used to decide whether a block runs or whether a loop continues.

  5. continue statement — a statement that skips the remaining statements of the current round of a loop and returns control to the top of the loop for the next round.

  6. Control structure — a programming construct that determines the flow of control of a program; Python supports two types, selection and repetition.

  7. Flow of control — the order in which the statements of a program are actually executed when it runs.

  8. Indentation — the leading whitespace, that is, the spaces or tabs, at the beginning of a statement; in Python it marks which statements belong to which block.

  9. Infinite loop — a loop whose condition never becomes false, so that it never terminates on its own.

  10. Inner loop — the loop written inside the body of another loop; it completes all its rounds afresh for every round of the outer loop.

  11. Iteration — the repeated execution of a set of statements in a program.

  12. Loop — a construct that carries out iteration, executing its block of statements again and again while a stated condition holds.

  13. Loop control variable — the variable whose value decides how a loop proceeds; in a for loop it receives each item of the sequence in turn, and in a while loop it is set and updated by the programmer.

  14. Nested if — an if statement written inside the block of another if statement, used to ask a question that arises only after the first has been answered.

  15. Nested loop — a loop contained within the body of another loop.

  16. Outer loop — the enclosing loop of a nested arrangement, one round of which contains a complete run of the inner loop.

  17. Repetition — the control structure that executes a block of statements more than once, deciding how many times it runs.

  18. Selection — the control structure that tests a condition and, on the strength of the result, decides whether a block of statements is executed at all.

  19. Sequence — the default flow of control, in which statements are executed one after another from top to bottom, each exactly once.

6.9 Points to Ponder

  1. Sequence, selection and repetition are enough to express any computation that can be programmed at all. If that is so, why does Python bother to offer two different looping constructs rather than one?

  2. Most languages let the programmer indent as carelessly as they like, and Python does not. What does a language gain by making layout part of its syntax, and what does it give up?

  3. A while loop that never ends is usually a bug, and yet the interpreter runs it without a word of warning. Could a language reasonably be expected to detect such a loop before running the program, or is there a reason no language does this?

  4. Every use of break can be rewritten as a loop with a more elaborate condition and no break at all. If the language does not need the statement, what is it really for?

  5. An if..elif..else chain and a set of separate if statements can produce identical output on the data you happen to test them with, and differ completely on data you have not tried. What does this suggest about how a program should be tested before you trust it?

  6. Three nested loops of a hundred rounds each mean a million executions of the innermost statement. At what point does a correct program stop being a usable one, and is that a question about the program or about the machine it runs on?

6.10 Objective Questions

  1. Which of the following is not a looping construct in Python? [1 mark]

    1. for
    2. while
    3. do..while
    4. a for loop nested inside another for loop
  2. How many times will the body of the loop for i in range(3, 12, 3): be executed? [1 mark]

    1. 2
    2. 3
    3. 4
    4. 9
  3. What is the value of list(range(8, 3, -2))? [1 mark]

    1. [8, 6, 4]
    2. [8, 6, 4, 3]
    3. [8, 5]
    4. []
  4. Fill in the blank: The leading whitespace at the beginning of a statement is called ______, and in Python it decides which statements belong to the same ______. [1 mark]

  5. State the output of the following fragment. [1 mark]

for i in range(4):
    if i == 2:
        continue
    print(i, end=" ")
  1. Assertion (A): A for loop that iterates over a list can never become an infinite loop. Reason (R): The number of rounds of a for loop is fixed by the length of the sequence before the first round begins. [1 mark]
    1. Both A and R are true, and R is the correct explanation of A.
    2. Both A and R are true, but R is not the correct explanation of A.
    3. A is true, but R is false.
    4. A is false, but R is true.
  2. Assertion (A): A break statement written inside an inner loop terminates both the inner and the outer loop. Reason (R): A break statement terminates the loop that immediately encloses it. [1 mark]
    1. Both A and R are true, and R is the correct explanation of A.
    2. Both A and R are true, but R is not the correct explanation of A.
    3. A is true, but R is false.
    4. A is false, but R is true.
  3. Match each keyword in Column A with its effect in Column B. [1 mark]
Column A Column B
a. elif i. Ends the current round of the loop and starts the next one
b. else ii. Ends the loop entirely
c. break iii. Tests a further condition when the earlier ones have failed
d. continue iv. Runs when no condition in the chain was true
  1. Write the full form of ACM. [1 mark]

6.11 Check Your Concepts

  1. Define the flow of control of a program, and name the two types of control structures that Python supports. [2 marks]

  2. What is an infinite loop? State one reason why a while loop may become one. [2 marks]

  3. State the purpose of the range() function, and give one example in which the step value is negative. [2 marks]

  4. Differentiate between the break and the continue statements, giving a short example of each. [3 marks]

  5. Explain, with a short example of your own, why indentation in Python is described as part of the syntax rather than as a matter of neatness. [3 marks]

  6. Explain how an if..elif..else chain is executed. A chain tests whether a mark is below 33, then below 60, then below 80. Explain what would go wrong if these three conditions were written in the reverse order, and state the general rule this illustrates. [4 marks]

  7. A for loop that walks a list stops on its own; a while loop need not. Give reasons for this difference, and state the three things every while loop must have if it is to terminate. [4 marks]

  8. Write a complete program that reads ten integers from the keyboard and prints how many of them were positive, how many negative and how many zero. Along with the program, explain in three or four sentences why a single loop containing one if..elif..else chain is a better solution than three separate loops that each read the numbers again. [5 marks]

6.12 Output & Debugging

  1. Predict the output of the following fragment, and state the value of n when the loop ends. [2 marks]
n = 15
while n > 0:
    print(n, end=" ")
    n = n - 4
  1. Predict the output of the following fragment, and state how many times the statement inside the if is executed. [2 marks]
total = 0
for k in range(1, 6):
    if k % 2 == 0:
        total = total + k
print(total)
  1. Predict the output of the following fragment, and state how many times the body of the inner loop is executed in all. [3 marks]
for i in range(1, 4):
    for j in range(1, 3):
        print(i * j, end=" ")
    print()
  1. The following fragment is meant to print four lines, but it does not. Identify the error, state exactly what the fragment does when it is run, and rewrite it correctly. [3 marks]
count = 1
while count <= 4:
    print("Row", count)
count = count + 1
  1. The following program will not run at all. Identify the error, name the kind of error it is, and rewrite the faulty line correctly. [2 marks]
marks = int(input("Enter marks: "))
if marks >= 90:
    print("Grade A")
elif marks >= 75
    print("Grade B")
else:
    print("Grade C")
  1. Predict the output of the following fragment, and state in one sentence what it computes in general for any string given to it. [3 marks]
word = "LOOP"
count = 0
for ch in word:
    if ch == "O":
        continue
    count = count + 1
    print(ch, end="")
print()
print(count)

6.13 Case-Based Questions

  1. A school library charges a fine on books returned late. There is no fine for a book kept up to 14 days. Beyond 14 days and up to 30 days, the fine is ₹2 for each day past the fourteenth. Beyond 30 days, the fine is ₹5 for each day past the thirtieth, in addition to the fine already due for days 15 to 30. The librarian has recorded, for five returned books, the number of days each was kept:
days_kept = [8, 21, 14, 30, 47]
a. Name the control structure needed to work out the fine for a single book from the number of days it was kept, and say how many branches it must have. [1 mark]

b. Write the statements that compute and store the fine for one book, given that the number of days is in a variable `days`. [2 marks]

c. Write the loop that uses your answer to part (b) to print the fine for every book in the list and, after the loop, the total fine collected. [2 marks]
  1. A bus depot records the number of passengers carried on one route on each day of a week:
passengers = [42, 58, 61, 39, 74, 55, 48]
a. Write the statements that find and print the highest number of passengers carried on any single day. Your solution must use a loop and must not use any built-in function that finds the largest value for you. [2 marks]

b. Write a loop that counts and prints how many days carried more than 50 passengers. [1 mark]

c. The depot manager asks what your program from part (a) would do on a day when the list is empty because the route did not run at all. State what happens, and explain why. [1 mark]

6.14 Programming Practice

  1. Write a complete program that reads a word from the keyboard and prints how many of its characters are vowels. Treat both upper case and lower case letters as vowels. Submit the program and a sample run for the input Repetition. [3 marks]

  2. Write a complete program that reads a whole number n from the keyboard and prints the sum of all the even numbers from 1 to n, both inclusive. The program must print a suitable message if n is less than 2. Submit the program and its output for n = 12. [3 marks]

  3. A weather station has recorded the maximum temperature, in degrees Celsius, on each of seven days. Write a complete program that reads the seven readings from the keyboard using a single loop and prints the highest reading and the number of days on which the reading was above 35. Submit the program together with an identifier description table. [4 marks]

  4. Write a complete program that uses nested loops to print a multiplication grid of five rows and five columns, in which the entry in row i and column j is the product i × j. The entries of a row must appear on one line, separated by spaces. Submit the program and its output. [4 marks]

  5. The Fibonacci sequence begins 0, 1, and every term after that is the sum of the two terms before it: 0, 1, 1, 2, 3, 5, 8 and so on. Write a complete program that reads a whole number n from the keyboard and prints the first n terms of the sequence, using a while loop. Submit the program, an identifier description table, and a note of two or three lines explaining how your loop produces each new term. Test your program with n = 7, and also with n = 1, and make sure it behaves sensibly in both cases. [5 marks]