How to Use List Comprehensions in Python
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:
# Numbers divisible by both 2 and 3
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']
The position of the if determines its purpose:
# Filtering (after 'for'): excludes items that don't match
[x for x in range(10) if x > 5] # [6, 7, 8, 9]
# Conditional expression (before 'for'): transforms every item
[x if x > 5 else 0 for x in range(10)] # [0, 0, 0, 0, 0, 0, 6, 7, 8, 9]
The filter version produces fewer items. The conditional expression version always produces the same number of items as the input.
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
# List comprehension
time_comp = timeit.timeit("[x ** 2 for x in range(1000)]", number=10000)
# Traditional loop
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
Exact times vary by machine, but comprehensions are consistently faster.
A list comprehension builds the entire list in memory at once. If you only need to iterate over the results and do not need to store them, use a generator expression instead. Generator expressions use parentheses instead of brackets and yield items one at a time:
# List comprehension: stores all 10 million items in memory
squares_list = [x ** 2 for x in range(10_000_000)]
# Generator expression: yields one item at a time
squares_gen = (x ** 2 for x in range(10_000_000))
# Use the generator in a loop or pass it to a function like sum()
total = sum(x ** 2 for x in range(10_000_000))
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:
# Wrong: builds a useless list just to print values
[print(x) for x in range(5)]
# Correct: use a regular loop when you don't need a list
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:
# Hard to read
result = [x.strip().lower().replace(" ", "_") for x in data if x.strip() and not x.startswith("#") and len(x) < 100]
# Better: extract the logic into a function
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]
# Even better for complex conditions: use a regular loop
result = []
for x in data:
stripped = x.strip()
if stripped and not stripped.startswith("#") and len(stripped) < 100:
result.append(stripped.lower().replace(" ", "_"))
Readability counts. A clear three-line for loop is always preferable to a cryptic one-liner that takes effort to parse. Keep comprehensions simple and self-explanatory.
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
ifclause after the iterable to filter which items are included. - Place an
if/elseexpression before theforkeyword when you need to transform every item conditionally. - Use multiple
forclauses 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.