Skip to content
Journal

Technology · Python

The Power of List Comprehensions in Python

Master Python list comprehensions including syntax, filtering, nested comprehensions, and dictionary/set comprehensions with practical code examples.

Anurag Verma

Anurag Verma

6 min read

The Power of List Comprehensions in Python

Sponsored

Share

List comprehensions in Python are a concise way to create a list. They consist of brackets containing an expression followed by a for clause, then zero or more for or if clauses. The result will be a new list resulting from evaluating the expression in the context of the for and if clauses which follow it. If you are still getting comfortable with core syntax like this, our Python variables and data types guide for beginners is a good place to start first.

One of the main benefits of using list comprehensions is that they are faster than using a for loop to create a list. That speedup comes from building the list in one pass instead of repeatedly looking up and calling append, per the Python tutorial’s section on list comprehensions.

The basic syntax

A list comprehension has one required shape: square brackets containing an expression, followed by a for clause, followed by zero or more additional for or if clauses. Python evaluates the expression once per iteration and collects every result into a new list, so the whole comprehension is a compact stand-in for a loop that builds a list one append at a time.

[expression for item in iterable]

This will create a new list containing the results of the expression for each item in the iterable. For example, to create a list of the squares of the numbers 0 through 9, we could use the following list comprehension:

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

This will create a new list containing the squares of the numbers 0 through 9, resulting in the list [0, 1, 4, 9, 16, 25, 36, 49, 64, 81].

Filtering with an if clause

We can also use if clauses to filter the items in the iterable. For example, to create a list of only the even squares, we could use the following list comprehension:

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

This will create a new list containing only the even squares, resulting in the list [0, 4, 16, 36, 64].

Nesting comprehensions

List comprehensions can also be nested, allowing us to create lists of lists or perform other complex operations. For example, to create a list of tuples (number, square) for the numbers 0 through 9, we could use the following list comprehension:

tuples = [(x, x**2) for x in range(10)]

This will create a new list containing tuples of the form (number, square), resulting in the list [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25), (6, 36), (7, 49), (8, 64), (9, 81)].

Performance and readability

It’s important to consider performance when using list comprehensions, especially for large lists or when using nested comprehensions. In general, list comprehensions are faster and more memory-efficient than using a for loop to create a list. However, if the expression in the list comprehension is complex or if the list comprehension is deeply nested, it may be more efficient to use a traditional for loop.

Multiple for clauses

In addition to the basic syntax shown above, list comprehensions can also include multiple for clauses. This allows you to create lists using the Cartesian product of multiple iterables. For example, to create a list of all possible pairs of numbers a and b where a is in the range 0 to 2 and b is in the range 0 to 4, we could use the following list comprehension:

pairs = [(a, b) for a in range(3) for b in range(5)]

This will create a new list containing all possible pairs of a and b, resulting in the list [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4)].

Dict and set comprehensions

List comprehensions can also be used to create dictionaries and sets. To create a dictionary using a list comprehension, you can use the following syntax:

{key_expression: value_expression for item in iterable}

To create a set using a list comprehension, you can use the following syntax:

{expression for item in iterable}

Both forms follow the same filtering and nesting rules as list comprehensions. For the full rundown of Python’s built-in containers, including where dicts and sets differ from lists under the hood, see our guide to Python’s built-in data structures.

Comprehensions vs. map() and filter()

Before comprehensions became idiomatic, the same job was often done with map() and filter():

even_squares = list(filter(lambda x: x % 2 == 0, map(lambda x: x**2, range(10))))

Compare that to the comprehension version from earlier:

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

The comprehension reads left to right in the order the logic runs: build this expression, for this loop, with this filter. map() and filter() nest function calls and lambdas that read inside-out, which is why most Python style guides favor a comprehension whenever the transformation fits on one line. map() still earns its place when you already have a named function instead of a lambda — map(str.upper, words) needs no throwaway loop variable, while the comprehension equivalent [w.upper() for w in words] introduces one just to discard it.

The walrus operator inside a comprehension

Python 3.8 added the walrus operator (:=), which lets a comprehension reuse an expensive computation instead of running it twice:

results = [y for x in data if (y := expensive(x)) > 0]

Without the walrus operator, filtering on a computed value and then using that same value in the output expression meant calling expensive(x) twice — once in the if and once in the expression — or falling back to a plain loop. The walrus operator computes expensive(x) once, binds it to y, and makes that result available to both the condition and the expression that follows it.

The short version

List comprehensions in Python are a concise way to create lists, dictionaries, and sets. They build the result in a single pass, and they let you express filtering and transformation logic in one line instead of several. The tradeoff is readability: past one level of nesting or one non-trivial condition, a plain for loop usually communicates the same logic more clearly.

Frequently asked questions

Are list comprehensions really more memory-efficient than a loop?
No, and this is a widespread misconception worth correcting. A comprehension builds the complete list in memory exactly as an append loop does; a million-element comprehension holds a million elements. What it saves is time, because the interpreter avoids repeatedly looking up and calling the append method. If memory is your constraint, the answer is a generator expression, which uses parentheses instead of brackets and yields values one at a time without ever materialising the full sequence.
When should I use a loop instead?
When the comprehension stops being readable, which happens sooner than people expect. Two or more levels of nesting, a complicated conditional, or an expression that needs a comment are all signs. A comprehension's advantage is that a reader takes it in as one thing; once they have to parse it line by line, the loop they would have written is clearer and no slower in any way that matters.
Where does the if go, and why does it matter?
After the for clause it filters, keeping only items that satisfy it. Before the for clause, as part of a conditional expression, it transforms, producing one value or another for every item. So the filtering form and the transforming form look similar and do different things, and the transforming form requires an else because an expression must always produce a value.
How do multiple for clauses order their output?
Exactly as nested loops would: the leftmost for is the outer loop and the rightmost is the inner one, so the last variable changes fastest. That ordering trips people up because it reads opposite to how some other languages' comprehension syntax works. The reliable check is to write the nested loops out mentally and confirm the sequence matches.
Can I build dictionaries and sets the same way?
Yes. Braces with a key and value separated by a colon give you a dict comprehension; braces with a single expression give you a set comprehension. The filtering and nesting rules are identical, so the syntax you learn for lists transfers directly. The one thing to watch is that both silently drop duplicates, a set by definition and a dict by later keys overwriting earlier ones, which can hide a bug in your source data.

Sponsored

Sponsored

Discussion

Join the conversation.

Comments are powered by GitHub Discussions. Sign in with your GitHub account to leave a comment.

Sponsored