How to Iterate Through a List in Python: A Complete Guide

In this tutorial, you will learn every way to iterate through a list in Python. I will cover the classic for loop, the Pythonic list comprehension, enumerate for index-value pairs, zip for parallel iteration, itertools for advanced patterns, and more.

By the end of this guide you will know exactly which technique to use and when. Each method comes with runnable code examples and a practical use case drawn from real Python projects.

TLDR – Jump Straight to the Method You Need

– For loop with in: iterate over elements directly – For loop with range and len: iterate by index – List comprehension: create new lists while iterating – enumerate: get both index and value – zip: iterate over multiple lists simultaneously – while loop: manual index control with exit condition – itertools.chain: flatten multiple lists into one iteration – List unpacking with *: splat operator for flat iteration

Introduction

A list in Python is an ordered, mutable collection of items. It is one of the most common data structures you will work with. Almost every Python script, data pipeline, or backend service deals with lists at some point.

Iteration simply means visiting each element in the list one by one so you can read it, transform it, or act on it in some way. Getting iteration right is foundational to writing clean Python code.

Python gives you more iteration tools than most languages. The right choice depends on whether you need the index, the value, multiple lists at once, or a filtered result. Using the wrong tool leads to verbose code that is harder to read and maintain.

Method 1: For Loop with the `in` Operator

This is the most common iteration pattern in Python. You do not need an index. You just want each element.


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

for fruit in fruits:
    print(fruit)

Output:


apple
banana
cherry
mango

Python binds each list element to the loop variable in sequence. No index arithmetic required and no risk of an off-by-one error.

Here is a more realistic example where you process each item in a loop.


prices = [29.99, 49.99, 19.99, 99.99]

total = 0.0
for price in prices:
    total += price

print(f"Total: ${total:.2f}")

Output:


Total: $199.96

Use this when you only need the values and not their positions. This pattern scales well to any list size and remains readable even when the list contains dozens of items.

Method 2: For Loop with `range` and `len`

Sometimes you need the index alongside the value. You can combine `range` with `len` to generate index positions programmatically.


colors = ["red", "green", "blue", "yellow"]

for i in range(len(colors)):
    print(f"Index {i}: {colors[i]}")

Output:


Index 0: red
Index 1: green
Index 2: blue
Index 3: yellow

`range(len(colors))` produces the sequence `0, 1, 2, 3`. You then use each index to access the list via subscript notation.

This pattern also lets you iterate with a step, which is useful for interleaved or sampled data.


numbers = [10, 20, 30, 40, 50, 60]

for i in range(0, len(numbers), 2):
    print(f"Index {i}: {numbers[i]}")

Output:


Index 0: 10
Index 2: 30
Index 4: 50

You access every second element by passing a step of `2` to `range`. This is useful when you want to sample data at regular intervals or process paired elements.

The main drawback is that this style is verbose. Python has cleaner alternatives like `enumerate` which give you both index and value without the index arithmetic. Reserve `range(len())` for situations where you genuinely need to use the index for something other than accessing the list element.

Method 3: List Comprehension

List comprehension is a compact Python idiom for creating a new list while iterating over an existing one. It replaces multi-line for loops that build lists with repeated append calls.

Here is the basic pattern.


squares = [x ** 2 for x in range(1, 6)]
print(squares)

Output:


[1, 4, 9, 16, 25]

You iterate over `range(1, 6)` and apply `x ** 2` to each element. The result is a new list assigned to `squares`. The entire operation fits on one line.

You can also filter during iteration with a conditional expression inside the comprehension.


numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

evens = [n for n in numbers if n % 2 == 0]
print(evens)

Output:


[2, 4, 6, 8, 10]

Only elements where the condition evaluates to true end up in the new list. The filtering happens inline without a separate loop.

Here is a more practical example that transforms a list of strings.


names = ["  alice  ", "bob", "  CHARLIE  "]

cleaned = [name.strip().title() for name in names]
print(cleaned)

Output:


['Alice', 'Bob', 'Charlie']

You strip whitespace and apply title case in a single readable line. Each name is processed identically and the result is a new list without modifying the original.

List comprehensions are faster than traditional loops in Python because they are optimized at the interpreter level. Use them whenever you want to produce a transformed or filtered list in one expression. Avoid nesting more than two levels of comprehension as that becomes difficult to read.

Method 4: `enumerate`

`enumerate` gives you both the index and the value in a single loop without managing the index manually. It is one of the most useful built-in functions for working with sequences.


tasks = ["review PR", "write tests", "deploy to staging", "update docs"]

for i, task in enumerate(tasks):
    print(f"{i + 1}. {task}")

Output:


1. review PR
2. write tests
3. deploy to staging
4. update docs

`enumerate` returns a tuple of `(index, value)` on each iteration. The default index starts at zero. You can change the start value by passing a second argument to enumerate.


data = ["alpha", "beta", "gamma"]

for i, item in enumerate(data, start=1):
    print(f"Item {i}: {item}")

Output:


Item 1: alpha
Item 2: beta
Item 3: gamma

This pattern shines when you need the index for printing, slicing another list, or updating values in place.


scores = [78, 92, 65, 88]
threshold = 80

for i, score in enumerate(scores):
    if score < threshold:
        scores[i] = threshold

print(scores)

Output:


[80, 92, 80, 88]

You update values in place using the index you get from enumerate. A standard `for value in list` loop does not give you this capability since you have no reference to the position.

Method 5: `zip`

`zip` lets you iterate over two or more lists side by side. Each iteration gives you one element from each list as a tuple.


names = ["Alice", "Bob", "Charlie"]
scores = [95, 82, 74]

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

Output:


Alice: 95
Bob: 82
Charlie: 74

`zip` stops when the shortest list is exhausted. If one list is longer than the others, the extra elements are silently ignored.


names = ["Alice", "Bob", "Charlie", "Diana"]
scores = [95, 82]

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

Output:


Alice: 95
Bob: 82

To handle unequal lengths without dropping data, use `itertools.zip_longest` instead.


from itertools import zip_longest

names = ["Alice", "Bob", "Charlie", "Diana"]
scores = [95, 82]

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

Output:


Alice: 95
Bob: 82
Charlie: N/A
Diana: N/A

`zip_longest` fills missing values with whatever you pass to `fillvalue`. This is useful for aligning mismatched datasets.

You can zip more than two lists at the same time.


products = ["Widget A", "Widget B", "Widget C"]
prices = [19.99, 29.99, 39.99]
stock = [150, 43, 8]

for product, price, qty in zip(products, prices, stock):
    print(f"{product} | ${price:.2f} | Qty: {qty}")

Output:


Widget A | $19.99 | Qty: 150
Widget B | $29.99 | Qty: 43
Widget C | $39.99 | Qty: 8

Three lists, one clean loop. This eliminates the need for nested index-based loops that manually align list positions.

Method 6: `while` Loop

A `while` loop gives you explicit control over the index. You manage the termination condition yourself, which is useful for loops that need to run until a specific condition is met rather than until the list is exhausted.


items = ["first", "second", "third"]
i = 0

while i < len(items):
    print(f"Position {i}: {items[i]}")
    i += 1

Output:


Position 0: first
Position 1: second
Position 2: third

This pattern is useful when your loop termination depends on something other than reaching the end of the list.


data = [12, 7, 23, 45, 3, 99, 8]
target = 45

i = 0
found = False
while i < len(data):
    if data[i] == target:
        print(f"Found {target} at index {i}")
        found = True
        break
    i += 1

if not found:
    print(f"{target} not in the list")

Output:


Found 45 at index 3

You stop the loop as soon as you find the target value. A standard `for in` loop does not give you this early-exit flexibility with the index. The explicit break condition makes the intent clear to anyone reading the code.

The downside is that `while` loops require you to manage the index manually. Mistakes here lead to infinite loops or index out of bounds errors. Use `enumerate` or `zip` when you do not need manual index control.

Method 7: `itertools.chain`

`itertools.chain` lets you iterate over multiple lists as if they were one continuous sequence. You do not need to concatenate the lists into a new structure; you just chain the iteration.


from itertools import chain

list_a = ["a", "b", "c"]
list_b = [1, 2, 3]
list_c = ["x", "y"]

for item in chain(list_a, list_b, list_c):
    print(item, end=" ")

Output:


a b c 1 2 3 x y

No new list is created. The chaining happens at the iterator level, which means memory usage stays constant regardless of the total size of all lists combined.

You can also chain iterators that contain different types without any issues.


from itertools import chain

numbers = [1, 2, 3]
words = ["one", "two", "three"]

for item in chain(numbers, words):
    print(item, end=" | ")

Output:


1 | 2 | 3 | one | two | three

This is useful when you have data spread across multiple lists and need to process everything in a single loop pass without first merging the lists into a new data structure.

Method 8: List Unpacking with the Splat Operator

Python lets you unpack all elements of a list directly into separate variables using the splat operator. This is especially useful when you want to separate the first element from the rest.


values = [10, 20, 30, 40, 50]

first, *rest = values

print(f"First: {first}")
print(f"Rest: {rest}")

Output:


First: 10
Rest: [20, 30, 40, 50]

The splat operator captures all remaining elements into a list. This lets you process the head and tail of a list without explicit index access.


command_parts = ["python", "script.py", "--verbose", "--output", "result.csv"]
executable, *args = command_parts

print(f"Executable: {executable}")
print(f"Arguments: {args}")

Output:


Executable: python
Arguments: ['script.py', '--verbose', '--output', 'result.csv']

You split the list into the command and its arguments in one line of code. No loop needed and no need to track indices manually.

For deeply nested list iteration, you can combine unpacking with loops.


matrix = [[1, 2], [3, 4], [5, 6]]

for a, b in matrix:
    print(f"Row: {a + b}")

Output:


Row: 3
Row: 7
Row: 11

Each inner list unpacks into two variables directly. The loop runs once per inner list, treating each pair as a single iteration unit.

Iterating Over Nested Lists

When you have lists within lists, you need nested iteration or flattened access depending on what you want to accomplish.


matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

for row in matrix:
    for element in row:
        print(element, end=" ")
    print()

Output:


1 2 3
4 5 6
7 8 9

This is the standard nested loop approach for visiting every element in a 2D list.

Alternatively, you can flatten a nested list with a comprehension.


flattened = [element for row in matrix for element in row]
print(flattened)

Output:


[1, 2, 3, 4, 5, 6, 7, 8, 9]

The comprehension flattens the 2D structure into a single flat list in one expression.

Comparison Table

Method Use When You Need
`for value in list` Values only, no index
`range(len(list))` Index and value, or step iteration
List comprehension Transform or filter into a new list
`enumerate` Index and value together cleanly
`zip` Multiple lists side by side
`while` loop Manual index control or conditional exit
`itertools.chain` Iterate multiple lists as one stream
List unpacking Split into head and tail variables
Nested comprehension Flatten nested lists

Common Mistakes

Modifying the list while iterating. This causes unexpected behavior in Python. The iterator's internal state gets out of sync with the list length.


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

for n in numbers:
    if n % 2 == 0:
        numbers.remove(n)

This may skip elements or behave inconsistently. Iterate over a copy of the list instead.


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

for n in numbers[:]:  # iterate over a copy
    if n % 2 == 0:
        numbers.remove(n)

Using `range` with a mutable list that changes size. If you remove or add elements during iteration, the indices become misaligned and you risk IndexError or missing elements.

Confusing `zip` with alignment. `zip` does not align lists by index when they have different lengths. It stops at the shortest list. Use `zip_longest` from itertools when you need all elements from all lists.

Unpacking mismatched counts. List unpacking with the splat operator requires at least one variable to receive every element. If you underspecify the variables, Python raises a ValueError.


a, b = [1, 2, 3]  # ValueError: too many values to unpack
first, *rest = [1]  # first=1, rest=[] — works fine

Performance Considerations

For most use cases, the performance difference between these methods is negligible. The choice should be driven by clarity and correctness rather than micro-optimizations.

List comprehensions are typically faster than equivalent for loops that build lists because they avoid repeated method calls like `list.append()`. If you are building a transformed list from scratch, use a comprehension.

`itertools` functions are lazy iterators. They do not create intermediate lists in memory. For very large datasets, this matters significantly.


# Builds an intermediate list in memory
combined = list_a + list_b + list_c

# No intermediate list, iterates on demand
for item in chain(list_a, list_b, list_c):
    pass

For small lists with fewer than a thousand elements, the difference is irrelevant. For large data pipelines processing millions of rows, lazy iteration keeps memory usage flat and prevents swapping.

When to Use Each Method

Use a plain `for value in list` loop when you simply need to act on each element. This covers the majority of iteration scenarios you will encounter.

Reach for `enumerate` whenever you need the index inside the loop. It is cleaner than `range(len(list))` and makes the intent explicit.

Choose list comprehension when you want to produce a new list in a single expression. It is the Pythonic way to transform and filter without explicit loop logic.

Choose `zip` when working with parallel lists. It replaces index juggling with readable tuple unpacking.

Use `while` only when the termination condition is dynamic and depends on something you compute inside the loop. Prefer for loops for simple iteration.

Reach for `itertools.chain` when you have separate lists that logically belong together in one iteration pass. It avoids creating merged copies.

Use list unpacking with `*rest` when you want to split a list into head and tail without writing explicit index logic.

FAQ

How do I skip the first element while iterating?

Use slicing to skip the first element.


items = ["header", "data1", "data2", "data3"]

for item in items[1:]:
    print(item)

Output:


data1
data2
data3

`items[1:]` returns a new list starting from index 1. The original list is unchanged.

How do I iterate over a list in reverse?

Use `reversed()` for a lazy reverse iterator or slice with `[::-1]` for a reversed copy.


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

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

Output:


5
4
3
2
1

Both `reversed()` and `items[::-1]` produce a reverse iteration without modifying the original list. `reversed()` is more memory-efficient for large lists since it returns an iterator rather than a copy.

How do I get the index and value in a list comprehension?

You cannot use `enumerate` inside a list comprehension directly for side effects. Instead, use a comprehension to build a list of tuples.


items = ["a", "b", "c"]
indexed = [(i, v) for i, v in enumerate(items)]
print(indexed)

Output:


[(0, 'a'), (1, 'b'), (2, 'c')]

Can I iterate over a list of dictionaries?

Yes. Each iteration gives you the dictionary object itself, not individual key-value pairs. Access the dictionary inside the loop using bracket notation.


records = [{"name": "Alice", "score": 92}, {"name": "Bob", "score": 85}]

for record in records:
    print(f"{record['name']}: {record['score']}")

Output:


Alice: 92
Bob: 85

How do I iterate over two lists of different lengths?

Use `zip` for lists of equal length. Use `zip_longest` from itertools for unequal lengths with a fillvalue.


from itertools import zip_longest

a = [1, 2, 3]
b = ["a", "b"]

for num, letter in zip_longest(a, b, fillvalue="?"):
    print(f"{num} - {letter}")

Output:


1 - a
2 - b
3 - ?

How do I break out of a loop early?

Use the `break` statement inside any loop to exit immediately.


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

for fruit in fruits:
    if fruit == "cherry":
        print("Found cherry, stopping")
        break
    print(fruit)

Output:


apple
banana
Found cherry, stopping

The loop stops at the `break` and does not process any remaining elements.

What is the difference between `continue` and `break`?

`continue` skips the rest of the current iteration and moves to the next element. `break` exits the loop entirely.


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

for n in numbers:
    if n == 3:
        continue  # skip this iteration
    if n == 5:
        break  # stop the loop
    print(n)

Output:


1
2
4

The value 3 is skipped with `continue`. The value 5 triggers `break` which stops the loop entirely.

Iteration is a fundamental skill in Python. Every method in this guide solves a specific problem and has an appropriate time and place. Start with the simple `for value in list` pattern and reach for `enumerate`, `zip`, or comprehensions when your problem demands it.

The more patterns you have in your toolkit, the cleaner your code will be. Each technique reduces the gap between your intent and the code you write.

If you found this useful, explore related articles on AskPython: List Comprehension in Python, Python Enumerate Explained, and Working with Multiple Lists in Python.

Ninad
Ninad

A Python and PHP developer turned writer out of passion. Over the last 6+ years, he has written for brands including DigitalOcean, DreamHost, Hostinger, and many others. When not working, you'll find him tinkering with open-source projects, vibe coding, or on a mountain trail, completely disconnected from tech.

Articles: 121