Python – Iteration(for/while loops)

Introduction

Iteration is one of the foundational concepts in programming. At its core, iteration means executing a block of code repeatedly — either a fixed number of times or until a condition is met. Without loops, you would have to write the same logic over and over for every element in a dataset, which is neither practical nor maintainable.

In Python, you will reach for a loop whenever you need to:

  • Process every item in a collection (list, dictionary, file lines, database rows)
  • Repeat an action until a condition changes (waiting for user input, polling a service)
  • Accumulate a result by examining elements one at a time (sums, searches, transformations)
  • Generate structured output like tables, reports, or formatted strings

Python provides two loop constructs: the for loop and the while loop. Alongside these, Python offers powerful built-in functions like enumerate(), zip(), and range() that make iteration concise and expressive. Let’s walk through each of these in depth.


For Loops

A for loop iterates over a sequence — any iterable object such as a list, tuple, string, dictionary, set, or range. The loop runs once for each element in the sequence, binding the current element to a variable you define.

Syntax

for variable in iterable:
    # do something with variable

The body of the loop is defined by indentation. Python does not use braces or keywords to delimit blocks — whitespace matters.

Iterating Over a List

This is the most common use of a for loop. Each element in the list is visited exactly once, in order.

fruits = ["apple", "banana", "cherry", "mango"]

for fruit in fruits:
    print(fruit)

Output:

apple
banana
cherry
mango

Iterating Over a String

Strings are sequences of characters, so a for loop walks through each character one at a time.

message = "Python"

for char in message:
    print(f"Letter: {char}")

Output:

Letter: P
Letter: y
Letter: t
Letter: h
Letter: o
Letter: n

Iterating with range()

The range() function generates a sequence of integers. It is the go-to tool when you need to loop a specific number of times or need numeric indices.

# range(stop) — 0 to stop-1
for i in range(5):
    print(i)  # 0, 1, 2, 3, 4

# range(start, stop) — start to stop-1
for i in range(2, 7):
    print(i)  # 2, 3, 4, 5, 6

# range(start, stop, step) — with a custom step
for i in range(0, 20, 3):
    print(i)  # 0, 3, 6, 9, 12, 15, 18

Notice that range() is exclusive of the stop value. This is by design and aligns with zero-based indexing. range(5) gives you exactly 5 elements: 0 through 4.

Iterating Over a Dictionary

When you iterate over a dictionary directly, you get its keys. To access values or both keys and values, use the .values() or .items() methods.

profile = {"name": "Folau", "age": 30, "role": "Software Engineer"}

# Iterating over keys (default behavior)
for key in profile:
    print(key)

# Iterating over values
for value in profile.values():
    print(value)

# Iterating over key-value pairs — most useful in practice
for key, value in profile.items():
    print(f"{key}: {value}")

Output of key-value iteration:

name: Folau
age: 30
role: Software Engineer

Iterating Over a Tuple and a Set

Tuples and sets are both iterable. The key difference is that sets are unordered, so the iteration order is not guaranteed.

# Tuple
coordinates = (10, 20, 30)
for coord in coordinates:
    print(coord)

# Set — order may vary between runs
languages = {"Python", "Java", "Go", "Rust"}
for lang in languages:
    print(lang)

While Loops

A while loop repeats a block of code as long as a condition evaluates to True. Use a while loop when you don’t know in advance how many iterations you need — the loop continues until the condition changes.

Syntax

while condition:
    # loop body

Example — counting up

count = 0
while count < 5:
    print(f"Count is {count}")
    count += 1

Output:

Count is 0
Count is 1
Count is 2
Count is 3
Count is 4

The critical thing with while loops is that you must ensure the condition eventually becomes False. If it doesn't, you have an infinite loop, and your program will hang.

Processing User Input Until Quit

A classic use case for while loops is reading user input until a sentinel value is entered.

while True:
    command = input("Enter a command (type 'quit' to exit): ")
    if command.lower() == "quit":
        print("Goodbye!")
        break
    print(f"You entered: {command}")

This pattern — while True with a break — is a clean way to handle input loops. The condition is always true, and the exit logic is handled explicitly inside the loop body.


break and continue Statements

These two statements give you fine-grained control over loop execution.

break — Exit the Loop Early

The break statement immediately terminates the innermost loop and resumes execution at the next statement after the loop.

numbers = [10, 25, 33, 47, 52, 68]

# Find the first number greater than 40
for num in numbers:
    if num > 40:
        print(f"Found it: {num}")
        break

Output:

Found it: 47

continue — Skip to the Next Iteration

The continue statement skips the rest of the current iteration and moves to the next one.

# Print only even numbers
for i in range(10):
    if i % 2 != 0:
        continue
    print(i)

Output:

0
2
4
6
8

Use continue when you want to skip certain elements without restructuring your loop with deeply nested if blocks. It keeps the code flat and readable.


The else Clause on Loops

This is a Python-specific feature that surprises many developers coming from other languages. Both for and while loops can have an else block. The else block executes only if the loop completes normally — that is, without hitting a break.

For loop with else

teams = ["Lakers", "Jazz", "Suns"]

for team in teams:
    print(team)
else:
    print("All teams processed successfully.")

Output:

Lakers
Jazz
Suns
All teams processed successfully.

Practical use — searching for an item

The else clause really shines when combined with break. Think of the else as "no break occurred":

target = "Celtics"
teams = ["Lakers", "Jazz", "Suns", "Warriors"]

for team in teams:
    if team == target:
        print(f"Found {target}!")
        break
else:
    print(f"{target} was not found in the list.")

Output:

Celtics was not found in the list.

If the break had been triggered, the else block would be skipped entirely. This eliminates the need for a separate "found" flag variable.

While loop with else

count = 0
while count < 5:
    print(count)
    count += 1
else:
    print("Loop completed without break.")

Nested Loops

You can place one loop inside another. The inner loop runs to completion for each iteration of the outer loop. Nested loops are common when working with multi-dimensional data or generating combinations.

Building a Multiplication Table

for i in range(1, 6):
    for j in range(1, 6):
        print(f"{i * j:4}", end="")
    print()  # new line after each row

Output:

   1   2   3   4   5
   2   4   6   8  10
   3   6   9  12  15
   4   8  12  16  20
   5  10  15  20  25

Nested loop with break

Keep in mind that break only exits the innermost loop. If you need to break out of multiple levels, you'll need a flag variable or refactor the logic into a function with a return.

# Find a specific cell in a matrix
matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

target = 5
found = False

for row_idx, row in enumerate(matrix):
    for col_idx, value in enumerate(row):
        if value == target:
            print(f"Found {target} at row {row_idx}, col {col_idx}")
            found = True
            break
    if found:
        break

enumerate() — Index and Value Together

When you need both the index and the value during iteration, use enumerate(). This is far cleaner than manually tracking an index counter.

languages = ["Python", "Java", "Go", "Rust"]

# Without enumerate — avoid this pattern
index = 0
for lang in languages:
    print(f"{index}: {lang}")
    index += 1

# With enumerate — preferred approach
for index, lang in enumerate(languages):
    print(f"{index}: {lang}")

# You can also set a custom start index
for index, lang in enumerate(languages, start=1):
    print(f"{index}. {lang}")

Output (with start=1):

1. Python
2. Java
3. Go
4. Rust

Use enumerate() whenever you catch yourself creating a manual counter variable. It is more Pythonic and eliminates an entire category of off-by-one bugs.


zip() — Parallel Iteration

The zip() function lets you iterate over two or more sequences simultaneously. It pairs up elements by position and stops when the shortest sequence is exhausted.

names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]

for name, score in zip(names, scores):
    print(f"{name} scored {score}")

Output:

Alice scored 85
Bob scored 92
Charlie scored 78

Zipping three sequences

names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
grades = ["B", "A", "C+"]

for name, score, grade in zip(names, scores, grades):
    print(f"{name}: {score} ({grade})")

If the sequences have different lengths and you want to iterate to the longest one, use itertools.zip_longest():

from itertools import zip_longest

names = ["Alice", "Bob", "Charlie"]
scores = [85, 92]

for name, score in zip_longest(names, scores, fillvalue="N/A"):
    print(f"{name}: {score}")

Output:

Alice: 85
Bob: 92
Charlie: N/A

Reverse Iteration with reversed()

To traverse a sequence in reverse order without modifying the original, use the built-in reversed() function.

numbers = [1, 2, 3, 4, 5]

for num in reversed(numbers):
    print(num)

Output:

5
4
3
2
1

You can also reverse a range:

for i in range(10, 0, -1):
    print(i)  # 10, 9, 8, ..., 1

Loop Comprehensions (Brief Introduction)

Python offers a concise syntax for creating new lists (and other collections) from loops. These are called list comprehensions and they can replace simple for loops that build a list.

# Traditional for loop approach
squares = []
for x in range(1, 6):
    squares.append(x ** 2)
print(squares)  # [1, 4, 9, 16, 25]

# List comprehension — same result, one line
squares = [x ** 2 for x in range(1, 6)]
print(squares)  # [1, 4, 9, 16, 25]

# With a condition — only even squares
even_squares = [x ** 2 for x in range(1, 11) if x % 2 == 0]
print(even_squares)  # [4, 16, 36, 64, 100]

Comprehensions also work with dictionaries and sets:

# Dictionary comprehension
word_lengths = {word: len(word) for word in ["Python", "Java", "Go"]}
print(word_lengths)  # {'Python': 6, 'Java': 4, 'Go': 2}

# Set comprehension
unique_lengths = {len(word) for word in ["Python", "Java", "Go", "Rust"]}
print(unique_lengths)  # {2, 4, 6}

Use comprehensions for simple transformations and filtering. If the logic gets complex with multiple conditions or side effects, a regular for loop is more readable.


Practical Examples

Summing Numbers in a List

expenses = [45.50, 120.00, 33.75, 89.99, 15.25]

total = 0
for expense in expenses:
    total += expense

print(f"Total expenses: ${total:.2f}")
# Output: Total expenses: $304.49

# Pythonic alternative using built-in sum()
total = sum(expenses)
print(f"Total expenses: ${total:.2f}")

Finding an Item in a Collection

employees = [
    {"name": "Alice", "department": "Engineering"},
    {"name": "Bob", "department": "Marketing"},
    {"name": "Charlie", "department": "Engineering"},
    {"name": "Diana", "department": "Sales"}
]

target_name = "Charlie"

for employee in employees:
    if employee["name"] == target_name:
        print(f"Found {target_name} in {employee['department']}")
        break
else:
    print(f"{target_name} not found.")

Building a Multiplication Table

size = 10

# Print header row
print("     ", end="")
for j in range(1, size + 1):
    print(f"{j:4}", end="")
print()
print("    " + "-" * (size * 4))

# Print each row
for i in range(1, size + 1):
    print(f"{i:3} |", end="")
    for j in range(1, size + 1):
        print(f"{i * j:4}", end="")
    print()

Processing User Input Until Quit

tasks = []

while True:
    action = input("Enter 'add', 'list', or 'quit': ").strip().lower()

    if action == "quit":
        print(f"Exiting. You have {len(tasks)} task(s).")
        break
    elif action == "add":
        task = input("Enter task description: ").strip()
        if task:
            tasks.append(task)
            print(f"Added: {task}")
        else:
            print("Task cannot be empty.")
    elif action == "list":
        if tasks:
            for i, task in enumerate(tasks, start=1):
                print(f"  {i}. {task}")
        else:
            print("No tasks yet.")
    else:
        print("Unknown command. Try again.")

Iterating Over a Dictionary to Build a Report

sales_data = {
    "January": 15000,
    "February": 18500,
    "March": 22000,
    "April": 19750,
    "May": 24300
}

print("Monthly Sales Report")
print("=" * 30)

total_sales = 0
best_month = ""
best_amount = 0

for month, amount in sales_data.items():
    print(f"  {month:12s} ${amount:,.2f}")
    total_sales += amount
    if amount > best_amount:
        best_amount = amount
        best_month = month

print("=" * 30)
print(f"  {'Total':12s} ${total_sales:,.2f}")
print(f"\nBest month: {best_month} (${best_amount:,.2f})")

The pass Statement

Loop bodies in Python cannot be empty. If you need a placeholder — perhaps during development when you haven't written the logic yet — use pass.

for item in range(10):
    pass  # TODO: implement processing logic

This is also useful as a no-op in exception handling within loops:

data = ["10", "abc", "30", "xyz", "50"]
numbers = []

for item in data:
    try:
        numbers.append(int(item))
    except ValueError:
        pass  # silently skip non-numeric values

print(numbers)  # [10, 30, 50]

Common Pitfalls

1. Infinite Loops

Forgetting to update the loop variable in a while loop is the most common source of infinite loops.

# BUG: count is never incremented — this runs forever
count = 0
while count < 10:
    print(count)
    # Missing: count += 1

# FIX: always ensure the condition variable changes
count = 0
while count < 10:
    print(count)
    count += 1

If you accidentally create an infinite loop in a terminal, press Ctrl + C to interrupt it.

2. Modifying a List While Iterating

Never add or remove elements from a list while iterating over it. This leads to skipped elements or index errors.

# BUG: modifying list during iteration
numbers = [1, 2, 3, 4, 5, 6]
for num in numbers:
    if num % 2 == 0:
        numbers.remove(num)  # skips elements!
print(numbers)  # [1, 3, 5] — looks correct but 4 was never checked

# FIX: iterate over a copy, or build a new list
numbers = [1, 2, 3, 4, 5, 6]
numbers = [num for num in numbers if num % 2 != 0]
print(numbers)  # [1, 3, 5]

# Alternative: iterate over a copy using slicing
numbers = [1, 2, 3, 4, 5, 6]
for num in numbers[:]:  # numbers[:] creates a shallow copy
    if num % 2 == 0:
        numbers.remove(num)
print(numbers)  # [1, 3, 5]

3. Off-by-One Errors with range()

range() is exclusive of the stop value. This trips up developers who expect inclusive behavior.

# Want to print 1 through 5
for i in range(5):
    print(i)  # Prints 0, 1, 2, 3, 4 — NOT 1 through 5

# FIX: adjust start and stop
for i in range(1, 6):
    print(i)  # Prints 1, 2, 3, 4, 5

Best Practices

  • Use for loops when the number of iterations is known — iterating over a collection, a range, or any finite sequence.
  • Use while loops for condition-based repetition — when you genuinely don't know how many iterations you need (user input, convergence algorithms, event polling).
  • Prefer enumerate() over manual index tracking — it is more readable, less error-prone, and explicitly communicates your intent.
  • Use zip() for parallel iteration — instead of indexing into multiple lists, zip them together for cleaner code.
  • Use list comprehensions for simple transformations — they are concise and performant. But don't overuse them: if a comprehension exceeds one line or requires complex logic, use a regular loop.
  • Name your loop variables meaningfullyfor student in students reads better than for s in students or for i in students.
  • Avoid deep nesting — if you find yourself writing three or more levels of nested loops, consider breaking the logic into helper functions.
  • Use break and continue judiciously — they are powerful but can make flow harder to follow if overused. One break per loop is a reasonable guideline.

Key Takeaways

  1. for loops iterate over sequences (lists, strings, ranges, dictionaries) and are your default choice for most iteration needs.
  2. while loops run as long as a condition is true — ideal for input processing, polling, or when the iteration count is unknown.
  3. break exits the loop immediately; continue skips to the next iteration.
  4. The else clause on a loop runs only if the loop completed without a break — useful for search patterns.
  5. enumerate() gives you index-value pairs without manual counters.
  6. zip() lets you iterate over multiple sequences in parallel.
  7. List comprehensions offer a concise one-liner alternative for building lists from loops.
  8. Watch out for infinite loops, modifying collections during iteration, and off-by-one errors with range().
  9. Write loops with clear variable names and minimal nesting — your future self (and your teammates) will thank you.

 

Source code on Github




Subscribe To Our Newsletter
You will receive our latest post and tutorial.
Thank you for subscribing!

required
required


Leave a Reply

Your email address will not be published. Required fields are marked *