How to Iterate Through a List in Python – 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: 126