Loops in Python
By the Syncfusion DevTutorials team · Last updated
Repeating work with for and while, counting with range(), break, continue and pass, nested loops, avoiding infinite loops, and the loop else.
Imagine marking thirty exam papers.
You pick up a paper, add up the marks, write the total on the front, and put it on the pile. Then you pick up the next one and do exactly the same thing. Thirty times.
The job is one set of steps, repeated once for each paper. You do not write out thirty separate sets of instructions.
Programs repeat work the same way, and the tool is called a loop.
The same work, once for each mark
marks = [78, 85, 60]
for mark in marks:
print("Mark:", mark, "- Passed:", mark >= 35)
Two lines of code did the work three times. Add a fourth mark to the list and run it again — the loop handles it without a single change.
What You Will Learn
In this lesson, you will learn:
- How to repeat work once for each item in a list
- How to count with
range(), including counting backwards - How to repeat while something stays true
- How to leave a loop early, skip one turn, or do nothing
- How to put one loop inside another
- What an infinite loop is and how to avoid writing one
- The
elsethat can follow a loop, and when it runs
A loop runs the same block of code once for each item, or until a test stops being true.
Introduction to Loops
What Are Loops?
A loop repeats a block of code. Each repeat is called an iteration.
You will also hear the word turn used in this lesson. A turn and an iteration are the same thing: one pass through the loop body.
Why Loops Are Used
Without a loop, repeated work has to be written out in full.
Three marks, written out three times
marks = [78, 85, 60]
print(marks[0])
print(marks[1])
print(marks[2])
That works for three marks. For thirty it is unreadable, and for a list whose length you do not know in advance it is impossible.
Any number of marks, one block of code
marks = [78, 85, 60, 92, 45, 67, 88]
for mark in marks:
print(mark)
The same two lines handle seven marks, or seven hundred.
Types of Loops in Python
Python has two.
| Loop | Use it when | Stops when |
|---|---|---|
for | you know what you are going through — a list, a range, some text | the items run out |
while | you do not know how many turns it will take | the condition becomes false |
A rough rule: counting or going through a collection is a for. Waiting for something to change is a while.
You will meet both loop types in this lesson, starting with for.
for Loop
What Is a for Loop?
A for loop takes each item of a collection in turn and runs its block once for each.
for variable in collection:
code that runs once per item
The line ends in a colon, and the block is indented, exactly like an if.
One turn for each item
subjects = ["Maths", "Science", "English"]
for subject in subjects:
print("Marking", subject)
print("All subjects marked")
The last line is not indented, so it runs once, after the loop has finished.
Iterating Over Sequences
A for loop works on anything Python can take items from one at a time.
A list, some text, and a pair
for mark in [78, 85]:
print("Mark:", mark)
for letter in "Asha":
print("Letter:", letter)
for value in ("maths", 78):
print("Value:", value)
Text gives you one character at a time, which is often a surprise the first time you see it.
A dictionary gives you its keys:
Going through a dictionary
report = {"maths": 78, "science": 85}
for subject in report:
print(subject, "->", report[subject])
.items() gives you the key and the value together, which usually reads better:
Keys and values together
report = {"maths": 78, "science": 85}
for subject, mark in report.items():
print(subject, "->", mark)
Loop Variables
The name after for is the loop variable. It holds one item at a time, and Python gives it a new value on every turn.
You choose the name. Choose one that says what the item is.
A name that explains itself
marks = [78, 85, 60]
for mark in marks:
print(mark, "out of 100")
for mark in marks reads as a sentence. for m in marks does not.
The loop variable still exists after the loop ends, holding the last item:
The variable outlives the loop
marks = [78, 85, 60]
for mark in marks:
pass
print("Last mark:", mark)
That is occasionally useful and more often a source of confusion, so do not rely on it.
range() Function
Creating Ranges
range() produces a run of whole numbers. It is what you use when you want to count rather than go through a collection.
Counting from zero
for number in range(5):
print(number)
range(5) gives 0, 1, 2, 3, 4. It starts at 0 and stops before 5, so there are exactly five numbers and 5 itself is not one of them.
Start, Stop, and Step Values
range() takes up to three numbers.
range(stop)
range(start, stop)
range(start, stop, step)
All three forms
print(list(range(5)))
print(list(range(1, 6)))
print(list(range(0, 10, 2)))
list() is used here only so the numbers can be printed in one go. In a loop you do not need it.
The stop value is never included. range(1, 6) ends at 5.
A negative step counts backwards:
Counting down
for number in range(5, 0, -1):
print(number)
print("Lift off")
Getting the direction wrong gives an empty range rather than an error:
A range that produces nothing
print(list(range(5, 0)))
for number in range(5, 0):
print("This never prints")
print("The loop body never ran")
range(5, 0) counts up from 5 towards 0, which it can never reach, so it produces nothing at all. The loop runs zero times and the program carries on with no complaint.
Using range() with Loops
Counting from 1 is the usual reason to give a start value:
Numbering the subjects
subjects = ["Maths", "Science", "English"]
for number in range(1, len(subjects) + 1):
print(number, subjects[number - 1])
That works, but Python has a neater way when you need the position and the item together:
enumerate gives position and item
subjects = ["Maths", "Science", "English"]
for number, subject in enumerate(subjects, start=1):
print(number, subject)
No arithmetic on the index, so no chance of an off-by-one mistake.
Use range() when you want numbers. Use the collection itself when you want its items, and enumerate() when you want both.
while Loop
What Is a while Loop?
A while loop repeats as long as its condition stays true.
while condition:
code that runs while the condition is true
Repeating while something is true
lives = 3
while lives > 0:
print("Lives left:", lives)
lives -= 1
print("Game over")
The condition is checked before each turn. When lives reaches 0 the test fails and the loop ends.
If the condition is false at the start, the body never runs at all:
A loop that never starts
lives = 0
while lives > 0:
print("This never prints")
print("Finished without looping")
Loop Conditions
The condition is any expression that gives True or False, exactly as in an if.
Counting up to a total
total = 0
marks = 0
while total < 200:
marks += 50
total += marks
print("Added", marks, "- total now", total)
print("Final total:", total)
Updating Conditions
Something inside the loop must change what the condition depends on. This is the whole difference between a loop that ends and one that does not.
The line that makes the loop end
countdown = 3
while countdown > 0:
print(countdown)
countdown -= 1
print("Done")
Take out countdown -= 1 and countdown stays 3 for ever. The condition stays true, and the loop never stops.
A safe habit when you write a while: write the line that changes the condition at the same moment you write the condition itself, before you write anything else in the body.
Loop Control Statements
break
break leaves the loop immediately. Nothing after it in the body runs, and no further turns happen.
Stopping as soon as you find it
marks = [78, 85, 20, 60]
for mark in marks:
if mark < 35:
print("Found a failing mark:", mark)
break
print("Checked", mark)
print("Search finished")
The loop stopped at 20 and never looked at 60.
break is how you stop as soon as you have your answer, instead of walking through the rest of a list for nothing.
continue
continue skips the rest of this turn and moves on to the next one. The loop itself keeps going.
Skipping one turn
marks = [78, 85, 20, 60]
for mark in marks:
if mark < 35:
continue
print("Passed:", mark)
print("Finished")
The failing mark was skipped, and the loop carried on to 60.
| Statement | What it does |
|---|---|
break | leaves the loop completely |
continue | skips the rest of this turn, starts the next |
pass | does nothing at all |
pass
pass does nothing. It exists because Python needs a block after a colon, and sometimes you have nothing to put there yet.
A placeholder that keeps the program running
marks = [78, 85, 60]
for mark in marks:
if mark >= 80:
pass
else:
print(mark, "is below 80")
Without pass, the empty if block would stop the program with an IndentationError.
Use it as a reminder of a branch you intend to fill in. It is not a way to skip a turn — that is continue.
Nested Loops
Nested for Loops
A loop inside a loop is a nested loop. The inner loop runs all the way through on every single turn of the outer one.
Every subject for every student
students = ["Asha", "Ravi"]
subjects = ["Maths", "Science"]
for student in students:
for subject in subjects:
print(student, "-", subject)
Two students and two subjects give four lines. The counts multiply: ten students and ten subjects would be a hundred.
A table built from two loops
for row in range(1, 4):
line = ""
for column in range(1, 4):
line += str(row * column) + "\t"
print(line)
The inner loop builds one row. The outer loop moves to the next row.
Nested while Loops
The same idea works with while, and now both counters have to be updated.
Two counters, both reset and both updated
row = 1
while row <= 3:
column = 1
while column <= 3:
print(row, column, end=" ")
column += 1
print()
row += 1
Notice column = 1 sits inside the outer loop. It has to be reset at the start of every row, or the inner loop would run only once and then find its condition already false.
A nested loop multiplies the work. Two lists of 1,000 items each give a million turns. Before nesting, ask whether one pass would do.
Infinite Loops
What Is an Infinite Loop?
An infinite loop is a loop whose condition never becomes false, so it never ends.
In a browser page like this one, an infinite loop freezes the tab. On your own computer you stop it with Ctrl+C. Either way, nothing else happens until you do.
So the example below is deliberately not runnable — reading it is enough:
lives = 3
while lives > 0:
print(lives)
lives is never changed, so lives > 0 is true for ever.
Common Causes
| Cause | Example | Fix |
|---|---|---|
| Nothing updates the condition | while lives > 0: with no lives -= 1 | change the value inside the loop |
| Updating in the wrong direction | countdown += 1 when counting down to zero | countdown -= 1 |
The update is skipped by a continue | continue placed before the counter changes | update the counter before the continue |
| The update sits outside the loop | the counter line is not indented | indent it into the loop body |
| A condition that cannot be reached | while total != 10: adding 3 each time | use >= or <= rather than != |
The last one is the sneaky one. Counting 0, 3, 6, 9, 12 skips straight past 10, so total != 10 is true for ever.
Preventing Infinite Loops
Three habits deal with almost all of them.
Write the update with the condition. The moment you write while countdown > 0:, write countdown -= 1 before anything else.
Prefer for when you can. A for loop over a list or a range ends by itself, because the items run out.
A loop that cannot run for ever
for countdown in range(3, 0, -1):
print(countdown)
print("Done")
Give an open-ended loop a limit. When a loop waits for something that might never happen, count the turns and stop at a sensible number.
A guard that stops a runaway
MAX_TRIES = 5
total = 0
tries = 0
while total != 10 and tries < MAX_TRIES:
total += 3
tries += 1
print("Try", tries, "- total", total)
if total != 10:
print("Gave up after", tries, "tries")
The condition total != 10 can never be satisfied by adding 3 at a time. Without the guard this loop would run for ever; with it, the program says so and moves on.
In a while loop, prefer < or > over !=. A counter can step straight past an exact value and never come back to it.
Loop else Clause
for-else
A loop can have an else. It runs when the loop finished normally — that is, when no break happened.
The else runs when nothing broke out
marks = [78, 85, 60]
for mark in marks:
if mark < 35:
print("Found a failing mark")
break
else:
print("Every mark passed")
No mark was below 35, so the loop ran to the end and the else ran.
while-else
The same rule applies to a while.
A while loop with an else
countdown = 3
while countdown > 0:
print(countdown)
countdown -= 1
else:
print("Countdown finished without interruption")
When else Executes
This is the part people get wrong, so it is worth stating plainly.
The else of a loop runs when the loop ends on its own. It does not run if a break stopped it.
It is nothing to do with the loop body being empty, and nothing to do with the condition being false at the start.
A break skips the else
marks = [78, 20, 60]
for mark in marks:
if mark < 35:
print("Found a failing mark:", mark)
break
else:
print("Every mark passed")
print("Checked")
The break fired, so the else was skipped and only Checked followed.
A loop that never runs at all still counts as finishing normally:
No turns at all, and the else still runs
for mark in []:
print("This never prints")
else:
print("The else still runs")
The honest summary: read else here as "if no break". The keyword is a poor name, and even experienced Python programmers avoid it for that reason. The same job is often clearer with a flag or a function.
Common mistakes
Each row is one mistake. The middle column is what you will actually see on screen.
| Mistake | What you see | Why it happens | Fix |
|---|---|---|---|
Nothing updates the while condition | The page or terminal freezes | The condition stays true for ever | Change the value inside the loop body |
| The update sits outside the loop | The loop never ends | The line is not indented into the body | Indent it into the loop |
Expecting range(5) to include 5 | Four turns where you wanted five | range() stops before the stop value | range(1, 6) for 1 to 5 |
range(5, 0) with no step | The loop body never runs, and no error | Counting up from 5 can never reach 0 | range(5, 0, -1) |
Using != in a counting condition | The loop never ends | The counter steps straight past the value | Use < or > instead |
| Forgetting to reset the inner counter | The inner loop runs only once | Its condition is already false after the first row | Reset it inside the outer loop |
continue placed before the update | The loop never ends | The line that changes the condition is skipped | Update the counter before the continue |
Expecting pass to skip a turn | Nothing is skipped | pass does nothing at all | Use continue |
Expecting the loop else to always run | A message that disappears sometimes | else is skipped whenever a break fires | Read it as "if no break" |
| Changing a list while looping over it | Items quietly skipped | The positions shift under the loop | Loop over a copy, or build a new list |
| Forgetting the colon | SyntaxError: expected ':' | for and while both open a block | End the line with : |
What to Remember
- A loop runs the same block of code once for each item, or until a test stops being true.
- A
forloop goes through a collection and ends when the items run out. - A
whileloop repeats while its condition is true, and checks the condition before each turn. range(stop)starts at 0.range(start, stop)never includes the stop value. A negative step counts backwards.enumerate()gives the position and the item together, with no arithmetic on the index.breakleaves the loop.continueskips the rest of this turn.passdoes nothing.- A nested loop runs the inner loop fully on every turn of the outer one, so the work multiplies.
- Something inside a
whileloop must change what the condition depends on, or it never ends. - Prefer
<or>to!=in a counting condition, and give an open-ended loop a limit. - The
elseafter a loop means "if no break", and it runs even when the loop had no turns at all.
Next: Functions