Python for Loops: How to Iterate Over Sequences and Ranges
A for loop runs a block of code once for each item in an iterable, such as a list, string, tuple, or range. Use it when you want to repeat work for every element without managing an index by hand. This article shows how Python steps through items, how range() fits in, and the mistakes that cause loops to behave differently than expected.
Difficulty: Beginner
Helpful to know first: basic Python syntax, variables, and how lists and strings store multiple values.
How Python for Loops Walk Through Items
Python's for loop is not a counter loop first and foremost. It is an iteration loop: Python asks an iterable for the next item, assigns that item to your loop variable, and then runs the loop body once for that item.
The simplest form looks like this:
for item in items:
print(item)Here, items can be any iterable object. On each pass, Python binds the next value to item, then executes the indented block. When there are no more items, the loop ends automatically.
That means you usually do not need to write the loop index yourself. Python already knows how to move forward through the collection.
Looping over a list
A list is the most common place to start. Each value is visited in order, from first to last.
names = ["Ada", "Grace", "Linus"]
for name in names:
print(name)
# Output:
# Ada
# Grace
# LinusThis prints each name on its own line. The loop variable name changes on every iteration, but the list itself stays in the same order.
Looping over a string
Strings are also iterable. Python visits one character at a time, which is useful for counting, filtering, or checking text.
word = "Python"
for letter in word:
print(letter)
# Output:
# P
# y
# t
# h
# o
# nPython does not split the string into words here. It steps through each character, including spaces if they exist.
Looping over a tuple or set
Tuples work like lists for iteration. Sets also work, but their order is not guaranteed to be the same every time you run the program.
coordinates = (10, 20, 30)
for value in coordinates:
print(value)
# Output:
# 10
# 20
# 30Because tuples preserve order, the output matches the tuple order. With a set, the same idea works, but the order is intentionally not something you should rely on.
Using range() to Repeat a Block a Fixed Number of Times
When you need to repeat an action a specific number of times, range() is the standard tool. It produces a sequence of integers, and for loops over those numbers.
for number in range(3):
print("Hello")
# Output:
# Hello
# Hello
# Hellorange(3) gives the values 0, 1, and 2. It does not include 3. This stop-before-end rule is one of the most important things to remember about Python loops.
range() with a start and stop value
You can choose where the sequence starts and ends.
for number in range(2, 5):
print(number)
# Output:
# 2
# 3
# 4This is useful when counting from a specific starting point, or when you want loop values that match a problem statement.
range() with a step
The third argument sets the step size. A positive step moves upward. A negative step moves downward.
for number in range(10, 4, -2):
print(number)
# Output:
# 10
# 8
# 6The loop stops before it reaches 4. Because the step is -2, Python counts backward.
Getting the Index and the Item at the Same Time
Sometimes you need both the position and the value. Use enumerate() instead of manually tracking a counter. It keeps the code cleaner and avoids index mistakes.
fruits = ["apple", "banana", "cherry"]
for index, fruit in enumerate(fruits):
print(index, fruit)
# Output:
# 0 apple
# 1 banana
# 2 cherryBy default, enumerate() starts counting at 0, which matches Python's usual indexing. You can change the starting number if needed:
for index, fruit in enumerate(fruits, 1):
print(index, fruit)
# Output:
# 1 apple
# 2 banana
# 3 cherryThis is a good choice for numbered lists, menus, and reports where you want human-friendly numbering.
Nested for Loops for Rows and Columns
A loop can contain another loop. This is common when you work with grids, tables, or combinations of values.
for row in range(2):
for column in range(3):
print(row, column)
# Output:
# 0 0
# 0 1
# 0 2
# 1 0
# 1 1
# 1 2The outer loop runs one full time for each row. For each row, the inner loop runs all its values. That is why nested loops can produce many more iterations than they first appear to.
How Python for Loops Behave in Detail
Several details matter once you move beyond the basics. These are the places where beginners often make incorrect assumptions.
The loop variable is reassigned on every iteration
The name after for gets a new value each time through the loop. After the loop ends, the variable still exists and keeps the last value assigned.
for number in range(3):
pass
print(number)
# Output:
# 2This surprises some people who expect the variable to disappear when the loop ends. In Python, the loop variable stays in the surrounding scope.
for loops do not make a copy of the iterable
The loop uses the iterable as it exists when iteration starts, but it does not automatically freeze all future changes. If you modify a mutable collection while looping over it, the results can be confusing.
numbers = [1, 2, 3]
for number in numbers:
if number == 2:
numbers.append(4)
print(number)
# Output:
# 1
# 2
# 3
# 4This kind of code is hard to reason about. A safer pattern is to loop over a copy if you need to change the original list, or collect changes in a separate list first.
If you need to remove items while iterating, build a new list with the values you want to keep.
range() does not create a list of all numbers immediately
range() is memory-efficient. It represents the sequence lazily instead of building a full list of integers right away. That is why it is efficient for large loops.
numbers = range(1_000_000)
print(type(numbers).__name__)
# Output:
# rangeYou can still iterate over it like a sequence, but it is not a list. If you truly need a list, convert it with list(range(...)).
break and continue change the flow of a loop
break stops the loop entirely. continue skips the rest of the current iteration and moves to the next item.
for number in range(5):
if number == 2:
continue
if number == 4:
break
print(number)
# Output:
# 0
# 1
# 3The value 2 is skipped, and the loop ends before printing 4. This control flow is useful for searching, filtering, and early exit.
Predict the Output
range() stops before the end value
What does this print?
for number in range(1, 4):
print(number)Show answer
It prints 1, 2, and 3 on separate lines. The start value is included, but the stop value is excluded.
# Output:
# 1
# 2
# 3The loop variable keeps the last value
What does this print?
for item in ["a", "b", "c"]:
pass
print(item)Show answer
The loop variable ends with the last item from the iterable, so this prints c.
# Output:
# cCommon Mistakes with Python for Loops
Using range() like the end value is included
Beginners often expect range(1, 5) to include 5. It does not. The stop value is excluded, which often causes off-by-one errors.
Problem: This loop prints only four numbers because the stop value is not part of the range.
for number in range(1, 5):
print(number)Fix: If you want 1 through 5, use range(1, 6).
for number in range(1, 6):
print(number)The corrected version works because the stop value moves one step past the last number you want.
Reusing a loop variable name outside the loop by accident
Sometimes a programmer uses the same variable name in later code and expects it to still hold an earlier value. In Python, the loop variable is still assigned after the loop ends.
Problem: The code assumes number is gone, but it still refers to the last loop value.
for number in range(3):
pass
print("Last number:", number)Fix: Use a different name if you do not want the later code to depend on the loop variable.
for index in range(3):
pass
print("Loop finished")The corrected version avoids using the loop variable after the loop ends.
Changing a list while looping over it
Adding or removing items from a list during iteration can make the loop skip values or visit unexpected ones.
Problem: This code appends to the same list it is reading, so the loop keeps seeing new items and becomes hard to predict.
numbers = [1, 2, 3]
for number in numbers:
if number == 2:
numbers.append(4)Fix: Loop over a copy if you need to modify the original list.
numbers = [1, 2, 3]
for number in numbers[:]:
if number == 2:
numbers.append(4)
print(numbers)
# Output:
# [1, 2, 3, 4]The copy gives the loop a stable snapshot of the original items while the real list changes separately.
Using the wrong indentation level
The loop body must be indented consistently. If the indentation is wrong, Python will stop before it runs the loop.
Problem: The second print() line is not indented, so Python cannot tell whether it belongs in the loop.
for number in range(3):
print(number)Fix: Indent every statement that belongs to the loop by the same amount.
for number in range(3):
print(number)The corrected version works because Python can now see the loop body clearly.
Find the Bug
This code is meant to print the even numbers from 0 to 4, but it has a subtle logic bug. Find it.
for number in range(5):
if number % 2 == 1:
print(number)Show answer
The bug is in the condition. The code prints odd numbers because it checks whether the remainder is 1, not 0. The loop itself is fine.
Fixed code:
for number in range(5):
if number % 2 == 0:
print(number)
# Output:
# 0
# 2
# 4The corrected version works because even numbers leave no remainder when divided by 2.
When to Use for Loops and When to Use Something Else
A for loop is best when you already have a collection or a finite range of values and want to process each one. If you need to repeat until a condition changes, a while loop may fit better.
| Use this | When it fits | Example |
|---|---|---|
| for loop | Count over items or a fixed range | Visit every name in a list |
| while loop | Repeat until something changes | Keep asking for input until it is valid |
| list comprehension | Build a new list from existing data | Make a list of squared numbers |
Use for when iteration is natural and readable. Use while when the number of repeats is not known in advance. Use a list comprehension when your goal is to create a new list in one expression.
Common Questions About Python for Loops
Why does Python use indentation instead of braces?
Python uses indentation to show which lines belong to the loop body. This makes the structure visible in the code itself and reduces the need for extra punctuation.
Can I loop over anything?
You can loop over any iterable object. That includes lists, strings, tuples, sets, dictionaries, files, and many objects from Python's standard library. If an object supports iteration, for can use it.
Should I always use enumerate() with lists?
No. Use enumerate() only when you need the index. If you only need the values, looping directly over the list is simpler and clearer.
Try It Yourself
Write a program that prints the numbers from 1 to 5, one per line, and then prints their total on the last line.
- Use a for loop.
- Keep a running total in a separate variable.
- Print the total after the loop ends.
Expected output:
1
2
3
4
5
Total: 15Hint: Start the total at 0, add each number inside the loop, and remember that range(1, 6) includes 1 through 5.
Show solution
total = 0
for number in range(1, 6):
print(number)
total += number
print("Total:", total)
# Output:
# 1
# 2
# 3
# 4
# 5
# Total: 15Summary
- A for loop visits each item in an iterable, one at a time.
- Use range() when you need a sequence of numbers, and remember that the stop value is excluded.
- Use enumerate() when you need both the index and the value.
- The loop variable keeps its last value after the loop ends.
- Do not change a list while looping over it unless you really understand the consequences.
- break ends the loop early, and continue skips to the next iteration.
Next, learn while loops so you can repeat work until a condition becomes false.