List comprehensions are one of the most elegant and powerful features of the Python language. They let you create new lists concisely by applying transformations and filters in a single line of code, replacing traditional loops and functions like map() and filter() in most cases.
If you are starting your Python journey, mastering list comprehensions will significantly improve the quality and readability of your code. This complete guide covers everything from basic syntax to advanced techniques, with practical examples you can apply right away.
What Are List Comprehensions?
A list comprehension is a syntactic construct that creates a new list by iterating over an existing sequence (or any iterable) and optionally applying a filter condition. The basic syntax is:
[new_expression for item in iterable if condition]
The expression new_expression is evaluated for each item that satisfies the condition (if present), and the result is added to the new list.
Basic Syntax
Let's start with the simplest example: creating a list of squares from 0 to 9.
Using a traditional for loop:
squares = []
for number in range(10):
squares.append(number ** 2)
print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
With a list comprehension:
squares = [number ** 2 for number in range(10)]
print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
The difference is striking: in a single line, we eliminated the need to initialize an empty list, write the loop, and call append(). The code is cleaner, more readable, and in most cases, faster.
According to the official Python documentation, list comprehensions consist of brackets containing an expression followed by a for clause and then zero or more for or if clauses.
Why Use List Comprehensions?
List comprehensions offer three main advantages over traditional loops:
- Conciseness: they reduce several lines of code to a single expression
- Readability: once you learn the syntax, the code clearly expresses the intent
- Performance: they are generally faster than equivalent
forloops
Let's analyze the performance difference with a practical example. Creating a list of even numbers from 0 to 999999:
import time
start = time.time()
even_loop = []
for i in range(1000000):
if i % 2 == 0:
even_loop.append(i)
end_loop = time.time()
start = time.time()
even_comp = [i for i in range(1000000) if i % 2 == 0]
end_comp = time.time()
print(f"For loop: {end_loop - start:.4f}s")
print(f"Comprehension: {end_comp - start:.4f}s")
In practice, list comprehensions are significantly faster because the operation runs in C under the hood, without the overhead of calling append() on each iteration. This performance gain is especially relevant when processing large datasets.
PEP 202, which introduced list comprehensions in Python 2.0, explains the motivation behind this feature: providing a more succinct and readable way to create lists based on existing sequences.
Practical Day-to-Day Examples
1. String Transformation
Converting a list of strings to uppercase:
names = ['ana', 'john', 'mary', 'peter']
upper_names = [name.upper() for name in names]
print(upper_names) # ['ANA', 'JOHN', 'MARY', 'PETER']
2. Filtering with Conditionals
Selecting only even numbers:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = [num for num in numbers if num % 2 == 0]
print(evens) # [2, 4, 6, 8, 10]
3. Conditional Transformation (if-else)
Unlike the filtering if, you can use if-else before the for clause to conditionally transform elements:
numbers = [1, 2, 3, 4, 5]
labels = ['even' if num % 2 == 0 else 'odd' for num in numbers]
print(labels) # ['odd', 'even', 'odd', 'even', 'odd']
4. Flattening Matrices
Turning a 2D matrix into a 1D list:
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [num for row in matrix for num in row]
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
5. Applying Functions to Elements
def celsius_to_fahrenheit(c):
return (c * 9/5) + 32
temps_c = [0, 10, 20, 30, 40]
temps_f = [celsius_to_fahrenheit(c) for c in temps_c]
print(temps_f) # [32.0, 50.0, 68.0, 86.0, 104.0]
6. Extracting Data from Dictionaries
students = [
{'name': 'Ana', 'grade': 8.5},
{'name': 'John', 'grade': 6.0},
{'name': 'Mary', 'grade': 9.2},
{'name': 'Peter', 'grade': 4.5}
]
passed = [student['name'] for student in students if student['grade'] >= 7.0]
print(passed) # ['Ana', 'Mary']
Nested List Comprehensions
Nested comprehensions are useful for working with multidimensional data structures. Let's create a 4x4 identity matrix:
n = 4
identity = [[1 if i == j else 0 for j in range(n)] for i in range(n)]
for row in identity:
print(row)
# [1, 0, 0, 0]
# [0, 1, 0, 0]
# [0, 0, 1, 0]
# [0, 0, 0, 1]
Another classic example: transposing a matrix:
matrix = [[1, 2, 3], [4, 5, 6]]
transposed = [[row[i] for row in matrix] for i in range(3)]
print(transposed) # [[1, 4], [2, 5], [3, 6]]
Comprehensions with Multiple Conditions
You can chain multiple if conditions:
numbers = range(1, 51)
filtered = [n for n in numbers if n % 2 == 0 if n % 5 == 0]
print(filtered) # [10, 20, 30, 40, 50]
# Equivalent to: if n % 2 == 0 and n % 5 == 0
List Comprehensions vs map() and filter()
Python provides map() and filter() from functional programming that can do similar things:
numbers = [1, 2, 3, 4, 5]
# Using map()
squares_map = list(map(lambda x: x ** 2, numbers))
# Using comprehension
squares_comp = [x ** 2 for x in numbers]
# Using filter()
evens_filter = list(filter(lambda x: x % 2 == 0, numbers))
# Using comprehension
evens_comp = [x for x in numbers if x % 2 == 0]
The Python community and Guido van Rossum himself recommend using list comprehensions over map() and filter() with lambda, since the code is more readable.
The Real Python tutorial on list comprehensions goes deeper into this comparison and provides detailed benchmarks.
Best Practices
1. Keep It Simple
If a list comprehension becomes too long or complex, prefer using a traditional loop. A good rule of thumb: if the comprehension exceeds 80 characters, consider breaking it into multiple lines or using a loop.
# Bad (too long)
result = [complicated_function(x, y) for x in list_x for y in list_y if condition_1(x) and condition_2(y) and validation(x, y)]
# Better (explicit loop)
result = []
for x in list_x:
for y in list_y:
if condition_1(x) and condition_2(y) and validation(x, y):
result.append(complicated_function(x, y))
2. Use Generators for Large Volumes
If you are processing a very large amount of data and don't need to store all results in memory, use a generator expression (parentheses instead of brackets):
# List comprehension (all in memory)
squares_list = [x ** 2 for x in range(10_000_000)]
# Generator expression (lazy evaluation)
squares_gen = (x ** 2 for x in range(10_000_000))
# Iterate on demand
for value in squares_gen:
if value > 100:
break
GeeksforGeeks has an excellent section comparing generators and list comprehensions with practical examples.
3. Avoid Side Effects
List comprehensions should be used to create new lists, not for their side effects. Avoid:
# Bad (side effect)
[print(item) for item in my_list]
# Good
for item in my_list:
print(item)
4. Prefer Comprehensions Over map()/filter() with lambda
Unless you already have a named function, comprehensions are more readable:
# Comprehension (readable)
results = [len(word) for word in words]
# map with lambda (less readable)
results = list(map(lambda w: len(w), words))
# map with existing function (ok)
results = list(map(len, words))
Advanced Use Cases
List Comprehensions with zip()
Combining parallel lists:
names = ['Ana', 'John', 'Mary']
ages = [25, 32, 28]
people = [f'{name} is {age} years old' for name, age in zip(names, ages)]
print(people) # ['Ana is 25 years old', 'John is 32 years old', 'Mary is 28 years old']
Comprehensions with else (Ternary Operator)
values = [10, -5, 0, 8, -3, 15]
classified = ['positive' if v > 0 else 'negative' if v < 0 else 'zero' for v in values]
print(classified) # ['positive', 'negative', 'zero', 'positive', 'negative', 'positive']
Comprehensions with enumerate()
words = ['python', 'seo', 'article', 'code']
with_indices = [(i, word.upper()) for i, word in enumerate(words)]
print(with_indices) # [(0, 'PYTHON'), (1, 'SEO'), (2, 'ARTICLE'), (3, 'CODE')]
When NOT to Use List Comprehensions
Despite their power, there are situations where list comprehensions are not the best choice:
- Very complex logic: loops with multiple nested conditionals and extensive transformations
- Debugging: it is easier to add
print()or use a debugger with traditional loops - Large volumes with limited memory: use generator expressions instead
- When you need to break out of the loop early
- When you need to modify the original list in place
The W3Schools tutorial provides a friendly introduction covering the fundamentals for beginners.
Comparison with Other Languages
If you come from other languages, here is a quick comparison:
JavaScript (Array.map + filter):
let numbers = [1, 2, 3, 4, 5];
let evenSquares = numbers.filter(n => n % 2 === 0).map(n => n ** 2);
// [4, 16]
Python (list comprehension):
numbers = [1, 2, 3, 4, 5]
even_squares = [n ** 2 for n in numbers if n % 2 == 0]
# [4, 16]
Python's syntax is generally considered cleaner for this specific operation.
Conclusion
List comprehensions are one of the most distinctive and useful features of Python. They let you write cleaner, faster, and more expressive code. Mastering this feature is essential for any Python programmer, whether you are a beginner or an experienced professional.
Remember the key takeaways:
- Use comprehensions to replace simple transformation and filter loops
- Keep your comprehensions readable and concise
- Prefer generator expressions for large datasets
- Avoid side effects inside comprehensions
- Know the limits: not everything should be a list comprehension
Programiz offers more interactive examples for you to practice and consolidate your knowledge.
Now that you understand the power of list comprehensions, practice with your own data. Try refactoring old loops from your projects to use comprehensions — you will be surprised by the difference in code quality. Keep studying and exploring the Python ecosystem to discover even more features that make this language so special.