11. Repeating Actions with while Loops
Programs often need to repeat actions multiple times. You've already seen how to make decisions with if statements in Chapter 8, but what if you need to perform an action repeatedly until a certain condition is met? This is where loops come in.
Python provides two main types of loops: while loops and for loops. In this chapter, we'll focus on while loops, which repeat a block of code as long as a condition remains true. The for loop, which is better suited for iterating over sequences, will be covered in Chapter 12.
Understanding while loops is fundamental to writing programs that can process data repeatedly, validate user input, implement game loops, and handle many other real-world programming scenarios.
11.1) The Structure of a while Loop
A while loop repeatedly executes a block of code as long as a specified condition evaluates to True. Once the condition becomes False, the loop stops, and the program continues with the code after the loop.
Basic while Loop Syntax
The structure of a while loop looks like this:
while condition:
# Code block to repeat
# This code runs as long as condition is TrueThe condition is any expression that evaluates to a boolean value (or can be interpreted as truthy or falsy, as we learned in Chapter 7). The indented code block beneath the while statement is called the loop body, and it executes repeatedly as long as the condition remains True.
Let's see a simple example:
# Count from 1 to 5
count = 1
while count <= 5:
print(f"Count is: {count}")
count = count + 1 # Increment count
print("Loop finished!")Output:
Count is: 1
Count is: 2
Count is: 3
Count is: 4
Count is: 5
Loop finished!How this works step by step:
- We initialize
countto1 - The condition
count <= 5is checked. Since1 <= 5isTrue, the loop body executes - Inside the loop, we print the current count and then increment it by 1
- After the loop body completes, Python returns to the
whilestatement and checks the condition again - This process repeats until
countbecomes6, at which point6 <= 5isFalse, and the loop terminates - The program continues with the code after the loop
The key insight is that the condition is checked before each iteration (each repetition of the loop body). If the condition is False initially, the loop body never executes at all:
count = 10
while count <= 5:
print("This will never print")
print("Loop skipped entirely")Output:
Loop skipped entirelySince 10 <= 5 is False from the start, the loop body never runs.
The Importance of Modifying the Loop Variable
For a while loop to eventually stop, something inside the loop must change the condition from True to False. This typically means modifying the variable(s) used in the condition. If you forget to do this, you create an infinite loop (which we'll discuss in detail in the next section).
Here's an example that demonstrates why updating the loop variable matters:
# Calculate the sum of numbers from 1 to 10
total = 0
number = 1
while number <= 10:
total = total + number # Add current number to total
number = number + 1 # Move to next number
print(f"The sum of numbers from 1 to 10 is: {total}")Output:
The sum of numbers from 1 to 10 is: 55In this example, we're accumulating a sum while progressing through numbers. Both total and number change with each iteration, but it's the modification of number that ensures the loop eventually terminates when number becomes 11.
while Loops with User Input
One practical use of while loops is processing user input until a specific condition is met. Let's create a simple number guessing game:
# Simple number guessing game
secret_number = 7
guess = 0
while guess != secret_number:
guess = int(input("Guess the number (1-10): "))
if guess < secret_number:
print("Too low! Try again.")
elif guess > secret_number:
print("Too high! Try again.")
else:
print("Correct! You guessed it!")This loop continues asking for guesses until the user enters the correct number. Each iteration processes one guess and provides feedback. The loop naturally terminates when guess equals secret_number, making the condition guess != secret_number become False.
while Loops with Multiple Conditions
You can use boolean operators (and, or, not) to create more complex loop conditions, as we learned in Chapter 9:
# Process input until user enters "quit" or reaches 5 attempts
attempts = 0
user_input = ""
while user_input != "quit" and attempts < 5:
user_input = input("Enter a command (or 'quit' to exit): ")
attempts += 1
if user_input == "quit":
print("Goodbye!")
else:
print(f"You entered: {user_input}")
print(f"Attempts remaining: {5 - attempts}")
if attempts >= 5 and user_input != "quit":
print("Maximum attempts reached.")This loop continues as long as both conditions are true: the user hasn't entered "quit" and they haven't exceeded 5 attempts. The loop terminates when either condition becomes false.
Visualizing while Loop Execution
Here's a flowchart showing how a while loop executes:
The loop creates a cycle where the condition is checked, the body executes if the condition is true, variables are updated, and then the condition is checked again. This cycle continues until the condition becomes false.
11.2) Infinite Loops and How to Avoid Them
An infinite loop is a loop that never terminates because its condition never becomes False. Infinite loops are one of the most common mistakes beginners make with while loops, and they can cause your program to hang indefinitely.
What Causes Infinite Loops?
The most common cause of infinite loops is forgetting to modify the variable(s) that affect the loop condition. Let's look at a problematic example:
# WARNING: Infinite loop - for demonstration only
# PROBLEM: count is never modified
count = 1
while count <= 5:
print(f"Count is: {count}")
# Missing: count += 1If you were to run this code, it would print "Count is: 1" forever because count remains 1, and 1 <= 5 is always True. The condition never changes.
How to recognize this is an infinite loop: Look at the loop condition (count <= 5) and then check if anything inside the loop body modifies count. If not, and the condition starts as True, you have an infinite loop.
Here's the corrected version:
# Correct version with proper increment
count = 1
while count <= 5:
print(f"Count is: {count}")
count += 1 # This ensures the loop eventually terminatesOutput:
Count is: 1
Count is: 2
Count is: 3
Count is: 4
Count is: 5Debugging Infinite Loops with Safety Limits
When developing code with loops, it's helpful to add a safety limit to prevent accidental infinite loops during testing:
# Safety limit during development
count = 1
iterations = 0
max_iterations = 100 # Safety limit
while count <= 5 and iterations < max_iterations:
print(f"Count is: {count}")
count += 1
iterations += 1
if iterations >= max_iterations:
print("WARNING: Maximum iterations reached. Check for infinite loop.")This pattern adds a counter that tracks how many times the loop has executed. If it reaches the safety limit, you know something is wrong with your loop logic. Once you're confident your loop works correctly, you can remove the safety limit.
Infinite Loops with User Input
Another common scenario for infinite loops involves user input validation:
# WARNING: Potential infinite loop - for demonstration only
# PROBLEM: If user never enters valid input, loop never ends
age = -1
while age < 0:
age = int(input("Enter your age: "))
# If user enters negative number, loop continuesThis loop works correctly if the user eventually enters a non-negative number, but it becomes an infinite loop if the user keeps entering negative values. While this might be acceptable for some programs (you want to keep asking until valid input is received), it's important to understand that the loop's termination depends entirely on user behavior.
A more robust approach might include a way to exit:
# Better approach with exit option
age = -1
while age < 0:
user_input = input("Enter your age (or 'quit' to exit): ")
if user_input.lower() == 'quit':
print("Exiting program.")
age = 0 # Set to valid value to exit loop
else:
age = int(user_input)
if age < 0:
print("Age must be non-negative. Please try again.")
if age > 0:
print(f"Your age is: {age}")Infinite Loops with Logical Errors
Sometimes infinite loops occur due to logical errors in how you update variables:
# WARNING: Infinite loop - for demonstration only
# PROBLEM: count is decremented instead of incremented
count = 1
while count <= 5:
print(f"Count is: {count}")
count -= 1 # MISTAKE: This makes count smaller, not largerThis creates an infinite loop because count starts at 1 and becomes 0, -1, -2, etc. Since negative numbers are always less than or equal to 5, the condition count <= 5 remains True forever.
The corrected version:
count = 1
while count <= 5:
print(f"Count is: {count}")
count += 1 # Correct: increment to eventually exceed 5Debugging Infinite Loops
If you accidentally create an infinite loop while running a Python script, you can stop it by pressing Ctrl+C (or Cmd+C on Mac) in your terminal. This sends a keyboard interrupt signal to Python, which stops the program.
How to avoid infinite loops:
- Always ensure the loop condition can become False: Check that variables in the condition are modified inside the loop
- Use the correct comparison operator: Make sure
<=,<,!=, etc., match your intent - Test with small values first: Before running a loop that might execute many times, test with small limits
- Add debug print statements: Temporarily print the loop variable to see how it changes:
- Use safety limits during development: As shown earlier, add a maximum iteration counter while testing
11.3) Using break and continue in while Loops
Python provides two special statements that give you more control over loop execution: break and continue. These statements allow you to alter the normal flow of a loop based on conditions that arise during execution.
11.3.1) The break Statement
The break statement immediately terminates the loop, regardless of the loop condition. When Python encounters break, it exits the loop entirely and continues with the code after the loop.
Here's a simple example:
# Exit loop when a specific value is reached
count = 1
while count <= 10:
if count == 5:
print("Reached 5, stopping loop")
break
print(f"Count: {count}")
count += 1
print("Loop exited")Output:
Count: 1
Count: 2
Count: 3
Count: 4
Reached 5, stopping loop
Loop exitedNotice that once count equals 5, the break statement executes, and the loop terminates immediately. The loop never reaches count = 6 even though the condition count <= 10 would still be True.
How break Changes Loop Flow
Understanding how break alters the normal loop execution is crucial. Here's a flowchart showing the difference:
The key point: break provides an immediate exit path from the loop, bypassing both the remaining loop body code and the condition check.
Practical Use of break: Input Validation
One of the most common uses of break is to exit a loop when valid input is received:
# Keep asking for valid input until received
while True:
age_input = input("Enter your age (must be positive): ")
# Try to convert to integer
try:
age = int(age_input)
# Check if valid
if age > 0:
print(f"Thank you! Your age is {age}")
break # Exit loop on valid input
else:
print("Age must be positive. Please try again.")
except ValueError:
print("That's not a valid number. Please try again.")
print("Input validation complete")This pattern uses while True: to create an intentional infinite loop, then uses break to exit when valid input is received. This is cleaner than trying to manage a complex loop condition. (Note: We're using try and except here, which we'll learn about in detail in Part VII. For now, just understand that it catches errors when converting input to an integer.)
break with Multiple Conditions
You can use break with complex conditions to exit a loop when any of several criteria are met:
# Search for a specific item in user input
search_term = "python"
attempts = 0
max_attempts = 5
while attempts < max_attempts:
user_input = input("Enter a word (or 'quit' to exit): ").lower()
attempts += 1
if user_input == 'quit':
print("User requested exit")
break
if user_input == search_term:
print(f"Found '{search_term}'!")
break
print(f"'{user_input}' is not '{search_term}'. Try again.")
print(f"Attempts remaining: {max_attempts - attempts}")
if attempts >= max_attempts:
print("Maximum attempts reached")This loop can exit in three ways:
- User enters "quit" (first
break) - User enters the search term (second
break) - Maximum attempts reached (loop condition becomes
False)
11.3.2) The continue Statement
The continue statement skips the rest of the current iteration and jumps back to the loop condition check. Unlike break, which exits the loop entirely, continue just skips to the next iteration.
Here's a basic example:
# Print only odd numbers from 1 to 10
count = 0
while count < 10:
count += 1
if count % 2 == 0: # If even number
continue # Skip the rest and go to next iteration
print(f"Odd number: {count}")Output:
Odd number: 1
Odd number: 3
Odd number: 5
Odd number: 7
Odd number: 9How this works:
countincrements at the start of each iteration- If
countis even (count % 2 == 0),continueexecutes - The
continuestatement skips theprintstatement and jumps back to thewhilecondition - If
countis odd,continuedoesn't execute, so theprintstatement runs
How continue Changes Loop Flow
Here's a flowchart showing how continue affects loop execution:
The crucial difference between break and continue:
- break: Exits the loop completely, jumping to code after the loop
- continue: Skips remaining code in current iteration, jumps back to condition check
Important Placement Note for continue
Notice that count += 1 comes before the continue. If we placed it after, even numbers would cause continue to skip the increment, creating an infinite loop:
# WARNING: Infinite loop - for demonstration only
# PROBLEM: continue skips the increment for even numbers
count = 0
while count < 10:
if count % 2 == 0:
continue # Skips everything below, including count += 1
count += 1 # MISTAKE: This never runs for even numbers
print(f"Odd number: {count}")When count is 0 (even), continue executes, skipping count += 1. The loop checks 0 < 10 again, and the cycle repeats forever.
The rule: Always ensure loop variables that affect the condition are updated before any continue statement that might skip the rest of the loop body.
Practical Use of continue: Filtering Data
The continue statement is useful when processing data and you want to skip certain items:
# Process only valid scores
score_count = 0
total_score = 0
attempts = 0
while attempts < 5:
score_input = input(f"Enter score {attempts + 1} (or 'skip' to skip): ")
attempts += 1
if score_input.lower() == 'skip':
print("Skipping this score")
continue # Skip to next iteration
try:
score = int(score_input)
if score < 0 or score > 100:
print("Score must be between 0 and 100. Skipping.")
continue # Skip invalid scores
# Valid score - process it
total_score += score
score_count += 1
print(f"Score recorded: {score}")
except ValueError:
print("Invalid input. Skipping.")
continue
if score_count > 0:
average = total_score / score_count
print(f"\nAverage of {score_count} valid scores: {average:.1f}")
else:
print("\nNo valid scores entered")This example demonstrates multiple uses of continue:
- Skip when user enters "skip"
- Skip when score is out of valid range
- Skip when input isn't a valid number
Each continue prevents the score from being added to the total, but the loop continues to the next attempt.
Combining break and continue
You can use both break and continue in the same loop for sophisticated control:
# Process numbers until sum exceeds 100, skipping negative numbers
total = 0
count = 0
while True:
number_input = input("Enter a number (or 'done' to finish): ")
if number_input.lower() == 'done':
print("User finished entering numbers")
break # Exit loop
try:
number = int(number_input)
if number < 0:
print("Negative numbers not allowed. Skipping.")
continue # Skip to next iteration
total += number
count += 1
print(f"Added {number}. Current total: {total}")
if total > 100:
print("Total exceeded 100. Stopping.")
break # Exit loop when limit reached
except ValueError:
print("Invalid input. Skipping.")
continue
print(f"\nFinal total: {total} (from {count} numbers)")This loop demonstrates:
breakto exit when user types "done"continueto skip negative numberscontinueto skip invalid inputbreakto exit when total exceeds 100
11.4) Using else with while Loops
Python has a unique feature that many other programming languages don't have: you can attach an else clause to a while loop. This else block executes only if the loop completes normally (meaning the loop condition becomes False without encountering a break statement).
Basic else with while Syntax
The syntax looks like this:
while condition:
# Loop body
else:
# This executes only if loop completes normally
# (not interrupted by break)Let's see a simple example:
# Count from 1 to 5 with else clause
count = 1
while count <= 5:
print(f"Count: {count}")
count += 1
else:
print("Loop completed normally")
print("Program continues")Output:
Count: 1
Count: 2
Count: 3
Count: 4
Count: 5
Loop completed normally
Program continuesThe else block executes because the loop ran until its condition (count <= 5) became False. The loop completed "normally" without being interrupted.
When else Does NOT Execute: The break Statement
The key behavior of the else clause is that it does not execute if the loop is terminated by a break statement:
# Search for a number with break
count = 1
target = 3
while count <= 5:
print(f"Checking: {count}")
if count == target:
print(f"Found {target}!")
break
count += 1
else:
print("Target not found in range")
print("Search complete")Output:
Checking: 1
Checking: 2
Checking: 3
Found 3!
Search completeNotice that the else block ("Target not found in range") did not execute because the loop was terminated by break. This is the crucial distinction: else runs only when the loop exits normally (condition becomes False), not when it exits via break.
Now let's see what happens when the target isn't found:
# Search for a number that doesn't exist
count = 1
target = 7 # Not in range 1-5
while count <= 5:
print(f"Checking: {count}")
if count == target:
print(f"Found {target}!")
break
count += 1
else:
print("Target not found in range")
print("Search complete")Output:
Checking: 1
Checking: 2
Checking: 3
Checking: 4
Checking: 5
Target not found in range
Search completeThis time, the loop completed all iterations without finding the target, so the condition count <= 5 eventually became False, and the else block executed.
How else Works with Loop Completion
Here's a flowchart showing the execution paths with the else clause:
The else block is only reached when the loop condition becomes False naturally. If break is encountered, the flow jumps directly past the else block to the code after the loop.
Practical Use: Search Operations
The else clause is particularly useful for search operations where you want to know whether something was found:
# Search for a valid password in a list of attempts
valid_password = "python123"
max_attempts = 3
attempts = 0
while attempts < max_attempts:
password = input(f"Enter password (attempt {attempts + 1}/{max_attempts}): ")
attempts += 1
if password == valid_password:
print("Access granted!")
break
else:
print("Access denied. Maximum attempts exceeded.")
print("Account locked.")If the user enters the correct password, break executes and the else block is skipped. If all attempts are used without success, the loop completes normally and the else block executes, indicating failure.
else with continue
The continue statement does not prevent the else block from executing. Only break does that:
# continue doesn't affect else execution
count = 0
while count < 5:
count += 1
if count == 3:
print(f"Skipping {count}")
continue # Skip to next iteration
print(f"Processing {count}")
else:
print("Loop completed normally (continue doesn't prevent this)")Output:
Processing 1
Processing 2
Skipping 3
Processing 4
Processing 5
Loop completed normally (continue doesn't prevent this)The else block executes because the loop completed normally. The continue statement only affects individual iterations, not the overall loop completion.
Comparing else with and without break
Let's see a side-by-side comparison:
# Example 1: Finding a prime number (with break)
print("Finding first number divisible by 7:")
number = 1
while number <= 20:
if number % 7 == 0:
print(f"Found: {number}")
break
number += 1
else:
print("No number divisible by 7 found in range")
print()
# Example 2: Checking all numbers (no break)
print("Checking all numbers for divisibility by 7:")
number = 1
while number <= 20:
if number % 7 == 0:
print(f"Found: {number}")
number += 1
else:
print("Finished checking all numbers")Output:
Finding first number divisible by 7:
Found: 7
Checking all numbers for divisibility by 7:
Found: 7
Found: 14
Finished checking all numbersIn the first example, break stops the loop after finding the first match, so else doesn't execute. In the second example, the loop checks all numbers and completes normally, so else executes.
When to Use else with while Loops
The else clause is most useful when:
- Search operations: You want to know if something was found or not
- Validation with limited attempts: You need to handle the case where all attempts are exhausted
- Processing with early exit: You want different behavior for "completed everything" vs "stopped early"
However, else with loops can be confusing for programmers coming from other languages (where this feature doesn't exist). Sometimes it's clearer to use a flag variable:
# Using else clause
attempts = 0
while attempts < 3:
password = input("Enter password: ")
attempts += 1
if password == "secret":
print("Access granted")
break
else:
print("Access denied")
# Equivalent using a flag variable (sometimes clearer)
attempts = 0
access_granted = False
while attempts < 3:
password = input("Enter password: ")
attempts += 1
if password == "secret":
print("Access granted")
access_granted = True
break
if not access_granted:
print("Access denied")Both approaches work. Choose the one that makes your code clearer for your specific situation.
Understanding else with while loops gives you another tool for writing clear, expressive code, especially in search and validation scenarios where you need to distinguish between "found" and "not found" or "success" and "failure after all attempts."
In this chapter, we've explored while loops in depth, learning how to:
- Structure loops that repeat code based on conditions
- Avoid infinite loops by properly updating loop variables
- Use
breakto exit loops early when needed - Use
continueto skip iterations based on conditions - Use
elseto handle cases where loops complete normally withoutbreak
These tools give you powerful control over repetition in your programs. In the next chapter, we'll learn about for loops, which provide a more convenient way to iterate over sequences like strings, lists, and ranges.