Back to Blog
Python

How to Iterate Over Lists in Python: for Loops, enumerate, zip, and Comprehensions

Learn how to iterate over lists in Python using for loops, enumerate, list comprehensions, zip, and generator expressions, with practical examples and tradeoffs.

PythonList IterationenumerateList ComprehensionzipPerformance
Python list iteration concepts illustrated with a loop and index markers

When you need to iterate over a list in Python, the for loop is usually the most direct approach. Depending on whether you also need indexes, parallel lists, or a new transformed list, Python offers several iteration patterns. This article walks through the most common ways to loop over a list, explains the tradeoffs, and gives practical guidance on choosing the right method.

The Basic for Loop

The simplest way to iterate over a list is to use a for loop directly on the list object. Python's iterator protocol handles retrieving each element in order.

fruits = ['apple', 'banana', 'cherry'] for fruit in fruits: print(fruit)

This loop assigns each element to fruit and executes the body. It is the most readable and idiomatic form for most cases. You don't need to manage an index or call len(); the loop stops automatically when the list is exhausted.

Use this pattern whenever you only need the values, not the position. It is also efficient because it avoids extra function calls and index lookups.

Iterating with an Index: range and len

Sometimes you need the index of each element, for example to modify the list in place or to compare elements with their neighbors. The classic approach uses range(len(list)).

numbers = [10, 20, 30] for i in range(len(numbers)): numbers[i] = numbers[i] * 2

range(len(numbers)) produces indices from 0 to len(numbers) - 1. Inside the loop you access the element with numbers[i]. This works, but it is verbose and can be error-prone if you use the wrong index variable.

A more Pythonic way to get both index and value is enumerate(), covered in the next section. Use range(len()) when you truly need to modify the list by index or need to control the iteration step explicitly.

Using enumerate for Index and Value

enumerate() is a built-in function that returns an iterator of tuples, each containing an index and the corresponding element. It avoids manual index management and is more readable.

colors = ['red', 'green', 'blue'] for index, color in enumerate(colors): print(f'{index}: {color}')

The loop unpacks each tuple into index and color. You can also specify a starting index with the start parameter:

for index, color in enumerate(colors, start=1): print(f'{index}. {color}')

This is the recommended way when you need both the element and its position. It is clearer than range(len()) and avoids the extra list[index] lookup. Its performance is comparable to a plain for loop in most Python versions, so there is little downside.

List Comprehensions for Transformation

List comprehensions provide a concise syntax for building a new list by applying an expression to each element. They combine iteration and transformation in one line.

numbers = [1, 2, 3, 4] squares = [n * n for n in numbers]

This is equivalent to:

squares = [] for n in numbers: squares.append(n * n)

The comprehension is more compact and can be faster than the equivalent for loop with append() because it avoids repeated .append() attribute lookups and the overhead of a separate append call. Exact performance depends on the Python version and workload.

You can also add a condition to filter elements:

even_squares = [n * n for n in numbers if n % 2 == 0]

If you only need to perform side effects, such as printing, and don't need a new list, prefer a regular for loop. List comprehensions are meant for building lists, not for general iteration.

Iterating Multiple Lists with zip

When you need to iterate over two or more lists in parallel, use zip(). It takes multiple iterables and returns an iterator of tuples, pairing elements by position.

names = ['Alice', 'Bob', 'Charlie'] scores = [85, 92, 78] for name, score in zip(names, scores): print(f'{name}: {score}')

zip() stops at the shortest list. If you need to iterate until the longest list and fill missing values, use itertools.zip_longest from the standard library. For equal-length lists, zip() is the cleanest solution.

This pattern is useful when data is split into parallel lists, as often happens when working with CSV-style columns or grouped data. It avoids manual index tracking and makes the pairing explicit.

Performance and Memory Considerations

The choice of iteration method can affect memory usage and speed, though the differences are often small. A plain for loop over a list is memory-efficient because it doesn't create intermediate collections. enumerate() also yields tuples lazily, so it doesn't build a separate list of indices.

List comprehensions can be faster than an equivalent for loop with append() because of internal optimizations, but they create a new list in memory. If you are processing a very large sequence and don't need to store the full result, a generator expression may be a better choice:

squares_gen = (n * n for n in numbers)

This produces values one at a time without building the entire list. Use it when you only need the result once, for example when passing it to sum() or iterating over it a single time.

range(len()) involves an extra indexing operation each iteration, so it can be slightly slower than enumerate() in some implementations, but the difference is negligible for most lists. Larger gains usually come from avoiding unnecessary copies and choosing the right tool for the task.

When to Use Which Approach

  • Use a plain for loop when you only need the values and want maximum readability.
  • Use enumerate() when you need both the index and the value.
  • Use range(len()) only when you must modify the list by index or need a custom step.
  • Use a list comprehension when you want to build a new list from an existing one, especially with a filter.
  • Use zip() when iterating over multiple lists in parallel.
  • Use a generator expression when you need to process elements lazily without storing all results.

These patterns cover the most common list iteration needs in Python. Choosing the right one makes your code more maintainable and often more efficient.

Python List Iteration: for Loops, enumerate, zip, and Comprehensions | RYUSLOG DEV