Skip to content

How to Use List Comprehensions in Python

Learn how Python list comprehensions work with syntax, filtering, nested loops, performance tips, and when to avoid them for cleaner, faster code.

List comprehensions are one of Python's most distinctive and powerful features. They allow you to create new lists from existing iterables in a single, readable expression, combining loop logic, filtering, and transformation into a compact syntax.

This guide covers everything you need to use list comprehensions effectively, from basic syntax and conditional filtering to nested loops, practical real-world examples, performance considerations, and knowing when a traditional for loop is the better choice.

Understanding the Basic Syntax

A list comprehension follows this general pattern:

new_list = [expression for item in iterable]

It reads almost like English: "give me expression for each item in iterable."

Here is a simple example that squares each number in a range:

squares = [x ** 2 for x in range(10)]
print(squares)

Output:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

The equivalent traditional loop requires more lines and an explicit .append() call:

squares = []
for x in range(10):
    squares.append(x ** 2)
print(squares)

Output:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Both produce the same result, but the comprehension is more concise and, as you will see later, also faster.

Adding Conditional Filtering

You can filter which elements make it into the final list by adding an if clause after the iterable:

even_squares = [x ** 2 for x in range(10) if x % 2 == 0]
print(even_squares)

Output:

[0, 4, 16, 36, 64]

Only values of x that pass the condition (x % 2 == 0) are squared and included. This is equivalent to:

even_squares = []
for x in range(10):
    if x % 2 == 0:
        even_squares.append(x ** 2)

You can also chain multiple if conditions. They act as logical AND:

results = [x for x in range(30) if x % 2 == 0 if x % 3 == 0]
print(results)

Output:

[0, 6, 12, 18, 24]

Using Conditional Expressions (if/else)

To apply different transformations based on a condition rather than filtering items out, use a ternary expression before the for keyword:

labels = ["even" if x % 2 == 0 else "odd" for x in range(5)]
print(labels)

Output:

['even', 'odd', 'even', 'odd', 'even']

Working with Nested Loops

List comprehensions support multiple for clauses. This is most commonly used to flatten nested structures. The loop order reads left to right, matching the order of equivalent nested for loops:

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

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

Output:

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

The equivalent nested loop makes the ordering clearer:

flattened = []
for row in matrix:        # outer loop comes first
    for num in row:       # inner loop comes second
        flattened.append(num)

Creating nested structures

You can also produce nested lists by placing a comprehension inside another comprehension:

multiplication_table = [[i * j for j in range(1, 4)] for i in range(1, 4)]
print(multiplication_table)

Output:

[[1, 2, 3], [2, 4, 6], [3, 6, 9]]

Here, the outer comprehension iterates over i, and for each i the inner comprehension builds a row by iterating over j.

Practical Examples

Filtering and transforming strings

words = ["Hello", "WORLD", "Python", "CODE"]

lowercase_long = [word.lower() for word in words if len(word) > 4]
print(lowercase_long)

Output:

['hello', 'world', 'python']

Extracting data from a list of dictionaries

users = [
    {"name": "Alice", "age": 30},
    {"name": "Bob", "age": 25},
    {"name": "Charlie", "age": 35},
]

senior_names = [user["name"] for user in users if user["age"] >= 30]
print(senior_names)

Output:

['Alice', 'Charlie']

Filtering file names by extension

files = ["report.pdf", "data.csv", "image.png", "notes.txt"]

text_files = [f for f in files if f.endswith((".txt", ".csv"))]
print(text_files)

Output:

['data.csv', 'notes.txt']

Cleaning and normalizing user input

raw_tags = ["  Python ", "JAVA", " javascript ", "", "  ", "Ruby"]

clean_tags = [tag.strip().lower() for tag in raw_tags if tag.strip()]
print(clean_tags)

Output:

['python', 'java', 'javascript', 'ruby']

The if tag.strip() filter removes empty strings and whitespace-only entries before the transformation runs.

Building a dictionary from two lists

Although this guide focuses on list comprehensions, it is worth noting that the same syntax works for dictionaries and sets:

keys = ["name", "age", "city"]
values = ["Alice", 30, "NYC"]

user_dict = {k: v for k, v in zip(keys, values)}
print(user_dict)

Output:

{'name': 'Alice', 'age': 30, 'city': 'NYC'}

Performance Considerations

List comprehensions are generally faster than equivalent for loops with .append(). This is because they are optimized at the bytecode level: the interpreter avoids repeated method lookups for .append() and uses an internal mechanism to build the list.

import timeit

time_comp = timeit.timeit("[x ** 2 for x in range(1000)]", number=10000)

time_loop = timeit.timeit(
    """
result = []
for x in range(1000):
    result.append(x ** 2)
""",
    number=10000,
)

print(f"Comprehension: {time_comp:.4f}s")
print(f"Loop:          {time_loop:.4f}s")

Output:

Comprehension: 2.1657s
Loop:          3.0289s

Common Mistakes to Avoid

Using a comprehension for side effects

List comprehensions are designed to build a new list. Using them solely to call a function for its side effect creates a throwaway list in memory and makes the intent unclear:

[print(x) for x in range(5)]

for x in range(5):
    print(x)

Overcomplicating the expression

If your comprehension becomes hard to read at a glance, break it out into a regular loop or a helper function:

result = [x.strip().lower().replace(" ", "_") for x in data if x.strip() and not x.startswith("#") and len(x) < 100]

def clean_entry(entry):
    cleaned = entry.strip().lower().replace(" ", "_")
    return cleaned

result = [clean_entry(x) for x in data if x.strip() and not x.startswith("#") and len(x) < 100]

result = []
for x in data:
    stripped = x.strip()
    if stripped and not stripped.startswith("#") and len(stripped) < 100:
        result.append(stripped.lower().replace(" ", "_"))

When to Use List Comprehensions vs. Regular Loops

Scenario Recommended Approach
Simple transformation or filter List comprehension
Building a new list from an iterable List comprehension
Complex logic with multiple branches Regular for loop
Side effects (printing, writing to file) Regular for loop
Very large datasets where you only iterate once Generator expression
Nested logic deeper than two levels Regular for loop or helper function

Summary

List comprehensions provide a concise, readable, and performant way to create lists in Python. To use them effectively:

  • Use the basic pattern [expression for item in iterable] for straightforward transformations.
  • Add an if clause after the iterable to filter which items are included.
  • Place an if/else expression before the for keyword when you need to transform every item conditionally.
  • Use multiple for clauses to flatten nested structures, reading the loops left to right.
  • Prefer generator expressions (...) over list comprehensions [...] when you do not need to store the entire result in memory.
  • Avoid using comprehensions for side effects or when the logic becomes too complex to read in a single expression.

With practice, list comprehensions become a natural and powerful part of writing clean, idiomatic Python code.

Navigation