# Python for Loops: How to Iterate Over Sequences and Ranges

Source: https://devdocs10.com/articles/python/python-for-loops  
By Murat Bo · Published 2026-11-14 · DevDocs10 (https://devdocs10.com)

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:

```python
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.

```python
names = ["Ada", "Grace", "Linus"]

for name in names:
    print(name)

# Output:
# Ada
# Grace
# Linus
```

This 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.

```python
word = "Python"

for letter in word:
    print(letter)

# Output:
# P
# y
# t
# h
# o
# n
```

Python 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.

```python
coordinates = (10, 20, 30)

for value in coordinates:
    print(value)

# Output:
# 10
# 20
# 30
```

Because 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.

```python
for number in range(3):
    print("Hello")

# Output:
# Hello
# Hello
# Hello
```

`range(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.

```python
for number in range(2, 5):
    print(number)

# Output:
# 2
# 3
# 4
```

This 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.

```python
for number in range(10, 4, -2):
    print(number)

# Output:
# 10
# 8
# 6
```

The 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.

```python
fruits = ["apple", "banana", "cherry"]

for index, fruit in enumerate(fruits):
    print(index, fruit)

# Output:
# 0 apple
# 1 banana
# 2 cherry
```

By default, `enumerate()` starts counting at 0, which matches Python's usual indexing. You can change the starting number if needed:

```python
for index, fruit in enumerate(fruits, 1):
    print(index, fruit)

# Output:
# 1 apple
# 2 banana
# 3 cherry
```

This 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.

```python
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 2
```

The 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.

```python
for number in range(3):
    pass

print(number)

# Output:
# 2
```

This 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.

```python
numbers = [1, 2, 3]

for number in numbers:
    if number == 2:
        numbers.append(4)
    print(number)

# Output:
# 1
# 2
# 3
# 4
```

This 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.

```python
numbers = range(1_000_000)

print(type(numbers).__name__)

# Output:
# range
```

You 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.

```python
for number in range(5):
    if number == 2:
        continue
    if number == 4:
        break
    print(number)

# Output:
# 0
# 1
# 3
```

The 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?

```python
for number in range(1, 4):
    print(number)
```

**Answer:**

It prints 1, 2, and 3 on separate lines. The start value is included, but the stop value is excluded.

```bash
# Output:
# 1
# 2
# 3
```

### The loop variable keeps the last value

What does this print?

```python
for item in ["a", "b", "c"]:
    pass

print(item)
```

**Answer:**

The loop variable ends with the last item from the iterable, so this prints `c`.

```bash
# Output:
# c
```

## Common 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.

```python
for number in range(1, 5):
    print(number)
```

**Fix:** If you want 1 through 5, use `range(1, 6)`.

```python
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.

```python
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.

```python
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.

```python
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.

```python
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.

```python
for number in range(3):
print(number)
```

**Fix:** Indent every statement that belongs to the loop by the same amount.

```python
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.

```python
for number in range(5):
    if number % 2 == 1:
        print(number)
```

**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:**

```python
for number in range(5):
    if number % 2 == 0:
        print(number)

# Output:
# 0
# 2
# 4
```

The 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:**

```python
1
2
3
4
5
Total: 15
```

**Hint:** Start the total at 0, add each number inside the loop, and remember that `range(1, 6)` includes 1 through 5.

**Solution:**

```python
total = 0

for number in range(1, 6):
    print(number)
    total += number

print("Total:", total)

# Output:
# 1
# 2
# 3
# 4
# 5
# Total: 15
```

## Summary

- 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.
