Programming with Python | Chapter 6: Control Flow – Loops (for, while)
Chapter Objectives
- Understand the purpose of loops for automating repetitive tasks.
- Learn to iterate over sequences (lists, tuples, strings) using the
for
loop. - Use the
range()
function to generate sequences of numbers forfor
loops. - Implement condition-based repetition using the
while
loop. - Control loop execution using
break
(to exit early) andcontinue
(to skip an iteration). - Understand and use the optional
else
block in loops. - Avoid infinite loops.
- Apply loops to solve common programming problems.
Introduction
In programming, we often need to perform the same or similar actions multiple times. Manually writing the code for each repetition would be tedious and inefficient. Loops provide a powerful mechanism to execute a block of code repeatedly until a certain condition is met or until we have processed all items in a sequence. This chapter introduces Python‘s two main types of loops: the for
loop, typically used for iterating over a known sequence of items, and the while
loop, used for repeating code as long as a specific condition remains true. We will also cover statements that allow finer control over loop execution: break
and continue
.
Theory & Explanation
Why Use Loops?
Imagine you need to print each item in a list of 100 names. Without loops, you’d need 100 print()
statements. With a loop, you write the print()
statement once inside the loop structure, and Python handles the repetition. Loops are fundamental for:
- Processing items in collections (lists, tuples, strings, dictionaries, sets).
- Repeating calculations until a condition changes.
- Reading data from files line by line.
- Running simulations or animations.
graph TD A[Start for loop] --> B{Any items left in sequence?}; B -- Yes --> C[Assign next item to variable]; C --> D[Execute Loop Block]; D --> B; B -- No --> E[Continue after for loop];
The for
Loop: Iterating Over Sequences
The for
loop is used to iterate over the items of any sequence (like a list, tuple, or string) or any other iterable object, executing a block of code once for each item.
Syntax:
for variable in sequence:
# Code block to execute for each item (indented)
# 'variable' takes the value of the current item in each iteration
statement1
statement2
# Code here executes after the loop finishes
How it works:
- The loop takes the first item from the
sequence
. - It assigns this item to the
variable
. - It executes the indented code block using the current value of
variable
. - It takes the next item from the
sequence
, assigns it tovariable
, and executes the block again. - This repeats until all items in the
sequence
have been processed.
fruits = ["apple", "banana", "cherry"]
print("Processing fruits:")
for fruit in fruits: # 'fruit' takes values "apple", "banana", "cherry" in turn
print(f"- {fruit.capitalize()}")
print("Loop finished.")
# Iterating over a string
message = "Hello"
for char in message:
print(char)
The range()
Function
Often, you need to loop a specific number of times or iterate over a sequence of numbers. The range()
function is perfect for this. It generates a sequence of numbers.
range(stop)
: Generates numbers from 0 up to (but not including)stop
.range(start, stop)
: Generates numbers fromstart
up to (but not including)stop
.range(start, stop, step)
: Generates numbers fromstart
up to (but not including)stop
, incrementing bystep
.
# Print numbers 0 to 4
print("Numbers 0-4:")
for i in range(5):
print(i)
# Print numbers 2 to 5
print("\nNumbers 2-5:")
for i in range(2, 6):
print(i)
# Print even numbers from 0 to 8
print("\nEven numbers 0-8:")
for i in range(0, 10, 2):
print(i)
# Using range to access list elements by index (less Pythonic usually)
numbers = [10, 20, 30]
print("\nAccessing list by index:")
for index in range(len(numbers)): # len(numbers) is 3, range(3) is 0, 1, 2
print(f"Index {index}: {numbers[index]}")
# More Pythonic way to get index and value: enumerate()
print("\nUsing enumerate():")
for index, value in enumerate(numbers):
print(f"Index {index}: {value}")
Note: While you can use range(len(sequence))
to loop using indices, iterating directly over the sequence (for item in sequence
) or using enumerate()
(which yields pairs of (index, value)
) is generally preferred as it’s more readable (“Pythonic”).
The while
Loop: Condition-Based Repetition
The while
loop repeatedly executes a block of code as long as a given boolean condition remains True
.
Syntax:
while condition:
# Code block to execute as long as condition is True (indented)
statement1
statement2
# IMPORTANT: Usually need to update variables involved in the condition
# to eventually make it False, otherwise -> infinite loop!
# Code here executes after the condition becomes False
How it works:
- The
condition
is evaluated. - If
True
, the indented code block is executed. - After the block finishes, the
condition
is evaluated again. - This repeats until the
condition
evaluates toFalse
. - Execution then continues with the code after the
while
loop block.
graph TD A[Start] --> B{Condition?}; B -- True --> C[Execute Loop Block]; C --> B; B -- False --> D[Continue after while];
count = 0
print("Counting up to 3 (using while):")
while count < 3:
print(f"Count is {count}")
count += 1 # Crucial step: Modify 'count' so the condition eventually becomes False
print("While loop finished.")
# Example: Waiting for specific input
user_input = ""
while user_input.lower() != "quit":
user_input = input("Enter command (or 'quit' to exit): ")
print(f"You entered: {user_input}")
print("Exited program.")
Infinite Loops: If the condition in a while
loop never becomes False
, the loop will run forever. This usually happens when the variables controlling the condition are not updated correctly within the loop block. You typically need to press Ctrl+C
in the terminal to stop an infinite loop.
Loop Control Statements: break
and continue
These statements provide more control over loop execution from within the loop block.
1. break
Immediately terminates the entire current loop (both for
and while
). Execution jumps to the first statement after the loop block.
print("\nFinding first multiple of 3 (using break):")
numbers = [1, 2, 4, 5, 6, 8, 9]
found_multiple = None
for num in numbers:
print(f"Checking {num}...")
if num % 3 == 0:
found_multiple = num
print(f"Found it! {num}")
break # Exit the loop immediately
# Code here runs after break or loop completion
if found_multiple:
print(f"The first multiple of 3 found was {found_multiple}.")
else:
print("No multiple of 3 found.")
graph TD subgraph Loop Block direction TB L_Start[Start of Loop Iteration] --> Check{Condition for break?}; Check -- Yes --> Break[break statement]; Check -- No --> Rest[Execute rest of loop block]; Rest --> L_End["End of Loop Iteration (continue normally)"]; end Break --> AfterLoop[Execute code immediately after the loop]; L_End --> LoopCheck[Go to next iteration / Check loop condition]; style Break fill:#f9f,stroke:#333,stroke-width:2px
2. continue
:
Immediately stops the current iteration of the loop and jumps to the next iteration. The rest of the code block for the current iteration is skipped.
print("\nPrinting only odd numbers (using continue):")
for i in range(10): # 0 to 9
if i % 2 == 0: # If the number is even...
continue # ...skip the rest of this iteration and go to the next i
# This print statement is only reached for odd numbers
print(i)
graph TD subgraph Loop Block direction TB L_Start[Start of Loop Iteration] --> Check{Condition for continue?}; Check -- Yes --> Continue[continue statement]; Check -- No --> Rest[Execute rest of loop block]; Rest --> L_End[End of Loop Iteration]; end Continue --> LoopCheck[Go immediately to next iteration / Check loop condition]; L_End --> LoopCheck; style Continue fill:#ccf,stroke:#333,stroke-width:2px
The else
Block in Loops
Both for
and while
loops can have an optional else
block. This block is executed only if the loop completes its iterations normally (i.e., not terminated by a break
statement).
Syntax:
for item in sequence:
# Loop body
if condition_for_break:
break
else:
# This block executes ONLY if the loop finished without hitting 'break'
print("Loop completed normally.")
while condition:
# Loop body
if condition_for_break:
break
else:
# This block executes ONLY if the loop condition became False (no 'break')
print("Loop completed normally.")
This is often useful for searching: the else
block can run if the item being searched for was not found (because break
wasn’t executed).
print("\nSearching for 'kiwi' (using loop else):")
fruits = ["apple", "banana", "cherry"]
search_item = "kiwi"
for fruit in fruits:
if fruit == search_item:
print(f"Found {search_item}!")
break
else:
# This runs only if the loop finishes without finding 'kiwi'
print(f"{search_item} not found in the list.")
search_item = "banana"
for fruit in fruits:
if fruit == search_item:
print(f"Found {search_item}!")
break
else:
print(f"{search_item} not found in the list.") # This line won't run now
graph TD A[Start Loop] --> B{Loop Condition / Items Left?}; B -- True / Yes --> C[Execute Loop Block]; subgraph Loop Block direction TB CheckBreak{Break condition met?} -- Yes --> Break[break]; CheckBreak -- No --> ContinueProcessing[Continue Processing]; end C --> CheckBreak; ContinueProcessing --> B; B -- False / No --> ElseBlock[Execute 'else' block]; Break --> AfterLoop["Continue after loop (skipping else)"]; ElseBlock --> AfterLoop; style ElseBlock fill:#cfc,stroke:#333,stroke-width:1px
Loop Comparison: for
vs. while
Feature | for Loop |
while Loop |
---|---|---|
Primary Use Case | Iterating over a sequence (list, tuple, string, range, etc.) or any iterable when the number of iterations is known or determined by the sequence length. | Repeating a block of code as long as a specific condition remains true. Often used when the number of iterations is not known beforehand. |
Syntax | for variable in iterable: |
while condition: |
Iteration Control | Automatically iterates through each item in the iterable. The loop variable takes the value of the current item. | Relies on a boolean condition. The condition must be manually managed within the loop (e.g., incrementing a counter, changing a flag) to eventually become false and terminate the loop. |
Common Helpers | range() for numeric sequences.enumerate() to get index and value. |
Often involves counters or flags initialized before the loop and updated inside. |
Risk of Infinite Loop | Generally lower risk, as it stops when the iterable is exhausted. (Can happen if iterating over a generator that never stops). | Higher risk if the condition is never met or the variables controlling it are not updated correctly within the loop body. |
Example Scenario | Processing each character in a string. Applying an operation to every element in a list. Looping exactly 10 times. |
Waiting for user input until ‘quit’ is entered. Running a simulation until a target state is reached. Reading from a file until the end is reached. |
Both loops can use break
to exit early and continue
to skip to the next iteration. Both can also have an optional else
block.
Code Examples
Example 1: for
loop with range
and enumerate
# for_loop_examples.py
# Summing numbers from 1 to 10
total = 0
for i in range(1, 11): # 1, 2, ..., 10
total += i
print(f"Sum of numbers 1 to 10 is: {total}")
# Processing a list with index and value
data = [100, 200, 300, 400]
print("\nProcessing data with enumerate:")
for index, value in enumerate(data):
print(f"Item at index {index} is {value}. Double is {value * 2}")
# Nested loops to create a multiplication table (up to 3x3)
print("\nMultiplication Table (3x3):")
for row in range(1, 4): # Rows 1, 2, 3
for col in range(1, 4): # Columns 1, 2, 3
# print() has an 'end' parameter to control what's printed at the end
# Default is '\n' (newline), we change it to a tab '\t'
print(f"{row * col}\t", end="")
print() # Print a newline after each row is complete
Explanation:
- The first loop uses
range(1, 11)
to iterate through numbers 1 to 10 and calculate their sum. - The second loop uses
enumerate(data)
to get both the index and value of items in thedata
list during iteration. - The third example demonstrates nested loops. The outer loop iterates through rows (1 to 3), and the inner loop iterates through columns (1 to 3) for each row.
print(..., end="\t")
prints the product followed by a tab instead of a newline, keeping items on the same line. The outerprint()
creates a newline after each row.
Example 2: while
loop with break
and else
# while_loop_examples.py
import random # Module for generating random numbers
attempts = 0
max_attempts = 5
secret_number = random.randint(1, 10) # Generate a random integer between 1 and 10
print(f"Guess the number between 1 and 10. You have {max_attempts} attempts.")
while attempts < max_attempts:
guess_str = input(f"Attempt {attempts + 1}: Enter your guess: ")
# Basic input validation
if not guess_str.isdigit():
print("Invalid input. Please enter a number.")
continue # Skip the rest of this iteration, ask for input again
guess = int(guess_str)
attempts += 1
if guess == secret_number:
print(f"Congratulations! You guessed the number {secret_number} in {attempts} attempts.")
break # Exit the loop since the guess is correct
elif guess < secret_number:
print("Too low.")
else:
print("Too high.")
else:
# This block runs ONLY if the while loop condition (attempts < max_attempts)
# becomes False, meaning the loop finished without a 'break'.
print(f"\nSorry, you've used all {max_attempts} attempts.")
print(f"The secret number was {secret_number}.")
print("Game over.")
Explanation:
- A
while
loop runs as long asattempts
is less thanmax_attempts
. - Inside the loop, user input is taken.
continue
is used to skip the rest of the iteration if the input is not a digit.attempts
is incremented only after a valid guess.- If the guess is correct, a message is printed, and
break
exits the loop. - If the loop completes all attempts without the user guessing correctly (i.e.,
break
was never called), theelse
block associated with thewhile
loop executes, revealing the number.
while
Loop Animation
Common Mistakes or Pitfalls
Infinite while Loops:
Forgetting to update the variable(s) controlling thewhile
loop's condition within the loop body.Off-by-One Errors with range():
Forgetting thatrange(n)
goes up ton-1
, or thatrange(start, stop)
does not includestop
.- Modifying List While Iterating: Modifying a list (e.g., removing items) while iterating over it directly with a
for
loop can lead to unexpected behavior (skipped items). It's often safer to iterate over a copy (for item in my_list[:]
) or use awhile
loop with indices, or build a new list. Incorrect break/continue Usage:
Usingbreak
whencontinue
was intended (or vice-versa), or placing them incorrectly, leading to wrong loop termination or skipping logic.Misunderstanding Loop else:
Assuming theelse
block runs if the loop condition is initially false (it doesn't run at all then) or assuming it runs even ifbreak
occurred (it doesn't).
Chapter Summary
- Loops automate repetitive execution of code blocks.
- The
for
loop iterates over items in a sequence (list, tuple, string, etc.) or other iterable.- Syntax:
for variable in sequence:
- Use
range(start, stop, step)
to generate numerical sequences. - Use
enumerate(sequence)
to get both index and value.
- Syntax:
- The
while
loop executes a block as long as acondition
isTrue
.- Syntax:
while condition:
- Requires careful management of the condition variable(s) inside the loop to avoid infinite loops.
- Syntax:
break
exits the current loop entirely.continue
skips the rest of the current iteration and proceeds to the next one.- Loops can have an
else
block that executes only if the loop completes normally (without abreak
).
Exercises & Mini Projects
Exercises
- Countdown: Use a
while
loop to print numbers from 10 down to 1. After the loop, print "Blast off!". - Factorial Calculator: Ask the user for a non-negative integer. Use a
for
loop andrange()
to calculate its factorial (n! = 1 * 2 * 3 * ... * n). Print the result. (Factorial of 0 is 1). - List Summation: Create a list of numbers. Use a
for
loop to iterate through the list and calculate the sum of all numbers. Print the sum. - Find First Even Number: Create a list of numbers. Use a
for
loop andbreak
to find and print the first even number in the list. If no even number is found, print a message indicating that. (Hint: Use the loopelse
block). - Skip Multiples of 3: Use a
for
loop,range(1, 21)
, andcontinue
to print all numbers from 1 to 20 except for those that are multiples of 3.
Mini Project: To-Do List Manager (Enhanced)
Goal: Enhance the To-Do List Manager from Chapter 5 using loops for better interaction and display.
Steps:
- Start with the code structure from the Chapter 5 Mini Project (initialize
tasks = []
). - Enclose the main menu logic (displaying options, getting input,
if-elif-else
for choices) inside awhile True:
loop. This creates an infinite loop that will keep the program running until the user explicitly chooses to exit. - Modify the Choice 4 (Exit) block:
- Instead of just printing "Exiting program.", add a
break
statement. This will terminate thewhile True:
loop and end the program gracefully.
- Instead of just printing "Exiting program.", add a
- Improve Choice 2 (View Tasks):
- Instead of just printing the list, use a
for
loop withenumerate()
to print the tasks with their index numbers (starting from 1 for user-friendliness). - Example inside the loop:
print(f"{index + 1}: {task}")
- Instead of just printing the list, use a
- Improve Choice 3 (Remove Task):
- After getting the user's input for the index to remove, remember that the user likely entered a 1-based index (like shown in View Tasks), but Python lists use 0-based indexing. Adjust the index accordingly (e.g.,
actual_index = user_index - 1
). - Perform the index validation using the
actual_index
. - Use
tasks.pop(actual_index)
if the index is valid.
- After getting the user's input for the index to remove, remember that the user likely entered a 1-based index (like shown in View Tasks), but Python lists use 0-based indexing. Adjust the index accordingly (e.g.,
Additional Sources: