Python List Comprehensions: Syntax and Examples

List comprehensions are Python's syntactic sugar for creating lists based on existing iterables. They condense what would typically require multiple lines of loop code into a single, readable...

Key Insights

  • List comprehensions provide a concise syntax that’s typically 30-50% faster than equivalent for loops due to optimized C implementation in CPython
  • The conditional placement matters: filtering conditions go at the end (if condition), while transformations use ternary operators in the expression (x if condition else y)
  • Comprehensions should improve readability—if you need more than two levels of nesting or complex logic, use regular loops instead

Introduction to List Comprehensions

List comprehensions are Python’s syntactic sugar for creating lists based on existing iterables. They condense what would typically require multiple lines of loop code into a single, readable expression. More importantly, they’re not just shorter—they’re faster and more Pythonic.

Here’s the difference in action:

# Traditional approach with for loop
squares = []
for x in range(10):
    squares.append(x ** 2)

# List comprehension approach
squares = [x ** 2 for x in range(10)]

# Both produce: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

The comprehension version is more declarative. You’re stating what you want (a list of squares) rather than how to build it (initialize list, loop, append). This matters when you’re scanning code—the intent is immediately clear.

Basic Syntax and Structure

The fundamental syntax follows this pattern:

[expression for item in iterable]

Read it left to right: “Create a list where each element is expression, computed for each item in iterable.”

The data flow works like this:

  1. Python iterates through iterable
  2. Each element becomes item
  3. expression is evaluated using item
  4. The result is added to the new list

Here are practical examples:

# Convert strings to uppercase
names = ['alice', 'bob', 'charlie']
upper_names = [name.upper() for name in names]
# ['ALICE', 'BOB', 'CHARLIE']

# Extract attributes from objects
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

people = [Person('Alice', 30), Person('Bob', 25), Person('Charlie', 35)]
ages = [person.age for person in people]
# [30, 25, 35]

# Perform calculations
prices = [19.99, 29.99, 39.99]
with_tax = [price * 1.08 for price in prices]
# [21.5892, 32.3892, 43.1892]

The expression can be any valid Python expression—method calls, arithmetic, function calls, even other comprehensions (though be careful with readability).

Adding Conditional Filtering

You can filter which elements to include by adding an if clause at the end:

[expression for item in iterable if condition]

The condition acts as a filter. Only items where condition evaluates to True are processed.

# Filter even numbers
numbers = range(20)
evens = [n for n in numbers if n % 2 == 0]
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

# Select strings by length
words = ['a', 'list', 'of', 'words', 'with', 'varying', 'lengths']
long_words = [word for word in words if len(word) > 4]
# ['words', 'varying', 'lengths']

# Extract values above threshold
temperatures = [72, 68, 75, 80, 65, 70, 78]
hot_days = [temp for temp in temperatures if temp > 75]
# [80, 78]

# Combine transformation and filtering
numbers = [-5, 3, -2, 8, -1, 6]
positive_squares = [n ** 2 for n in numbers if n > 0]
# [9, 64, 36]

This filtering approach is cleaner than using filter() with lambda functions and more efficient than building a list then filtering it in a second pass.

Nested Loops and Complex Comprehensions

You can include multiple for clauses to iterate over nested structures or create combinations:

[expression for item1 in iterable1 for item2 in iterable2]

This is equivalent to nested for loops where the rightmost for is the innermost loop.

# Flatten a matrix
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened = [num for row in matrix for num in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

# Create coordinate pairs
x_coords = [0, 1, 2]
y_coords = [0, 1, 2]
coordinates = [(x, y) for x in x_coords for y in y_coords]
# [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

# Process nested data structures
departments = [
    {'name': 'Engineering', 'employees': ['Alice', 'Bob']},
    {'name': 'Sales', 'employees': ['Charlie', 'David', 'Eve']}
]
all_employees = [emp for dept in departments for emp in dept['employees']]
# ['Alice', 'Bob', 'Charlie', 'David', 'Eve']

You can also add conditions to each level:

# Only pairs where x < y
pairs = [(x, y) for x in range(5) for y in range(5) if x < y]
# [(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]

Using if-else in the Expression

When you want to transform values conditionally (not filter them out), use a ternary operator in the expression:

[expression_if_true if condition else expression_if_false for item in iterable]

This is different from filtering. Every item produces an output—the condition determines which output.

# Replace negative values with zero
numbers = [5, -3, 8, -1, 2, -7]
non_negative = [n if n >= 0 else 0 for n in numbers]
# [5, 0, 8, 0, 2, 0]

# Categorize values
scores = [85, 92, 67, 74, 95, 88]
grades = ['Pass' if score >= 70 else 'Fail' for score in scores]
# ['Pass', 'Pass', 'Fail', 'Pass', 'Pass', 'Pass']

# Apply different transformations
values = [1, 2, 3, 4, 5, 6]
transformed = [x * 2 if x % 2 == 0 else x * 3 for x in values]
# [3, 4, 9, 8, 15, 12]

You can combine this with filtering, but be careful with readability:

# Transform evens, filter out odds entirely
numbers = range(10)
doubled_evens = [n * 2 for n in numbers if n % 2 == 0]
# [0, 4, 8, 12, 16]

Performance and Best Practices

List comprehensions are faster than equivalent for loops because they’re optimized at the C level in CPython:

import timeit

# For loop approach
def with_loop():
    result = []
    for i in range(1000):
        result.append(i ** 2)
    return result

# Comprehension approach
def with_comprehension():
    return [i ** 2 for i in range(1000)]

loop_time = timeit.timeit(with_loop, number=10000)
comp_time = timeit.timeit(with_comprehension, number=10000)

print(f"Loop: {loop_time:.4f}s")
print(f"Comprehension: {comp_time:.4f}s")
print(f"Speedup: {loop_time/comp_time:.2f}x")
# Typical result: Comprehension is 1.3-1.5x faster

However, don’t sacrifice readability for brevity. This comprehension is too complex:

# DON'T DO THIS
result = [y for x in matrix if sum(x) > 10 for y in x if y % 2 == 0 and y > 5]

# DO THIS INSTEAD
result = []
for row in matrix:
    if sum(row) > 10:
        for value in row:
            if value % 2 == 0 and value > 5:
                result.append(value)

Guidelines for readable comprehensions:

  • Maximum two levels of nesting
  • Keep conditions simple
  • If you need comments to explain it, use a regular loop
  • Don’t perform side effects (file I/O, printing, modifying external state)

Dictionary and Set Comprehensions

Python extends comprehension syntax to dictionaries and sets:

# Dictionary comprehension
keys = ['a', 'b', 'c']
values = [1, 2, 3]
my_dict = {k: v for k, v in zip(keys, values)}
# {'a': 1, 'b': 2, 'c': 3}

# Square numbers as dict
squares_dict = {x: x ** 2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# Invert a dictionary
original = {'a': 1, 'b': 2, 'c': 3}
inverted = {v: k for k, v in original.items()}
# {1: 'a', 2: 'b', 3: 'c'}

# Set comprehension (removes duplicates)
numbers = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
unique_squares = {x ** 2 for x in numbers}
# {1, 4, 9, 16}

# Extract unique lengths
words = ['apple', 'pie', 'banana', 'cherry', 'plum']
lengths = {len(word) for word in words}
# {3, 4, 5, 6}

Dictionary comprehensions are particularly useful for transforming or filtering existing dictionaries:

prices = {'apple': 0.50, 'banana': 0.30, 'cherry': 0.75, 'date': 0.90}

# Filter expensive items
expensive = {k: v for k, v in prices.items() if v > 0.60}
# {'cherry': 0.75, 'date': 0.90}

# Apply discount
discounted = {k: v * 0.9 for k, v in prices.items()}
# {'apple': 0.45, 'banana': 0.27, 'cherry': 0.675, 'date': 0.81}

Master comprehensions and you’ll write more concise, performant Python. Use them for straightforward transformations and filtering. When logic gets complex, fall back to explicit loops. Your future self will thank you.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.