List comprehensions are one of Python’s most recognizable features. They create a new list from an iterable while expressing transformation and optional filtering in a compact form. Used well, they remove repetitive setup and make simple data pipelines easy to read. Used poorly, they compress too much logic into one difficult line.
This guide explains Python list comprehensions from the basic syntax through conditions, nested loops, dictionaries, sets, generator expressions, and practical style decisions. It assumes you know the fundamentals of Python loops and lists.
The basic pattern
A list comprehension has the form [expression for item in iterable]. Python evaluates the expression for every item and collects the results in a new list.
numbers = [1, 2, 3, 4, 5]
squares = [number ** 2 for number in numbers]
print(squares)The equivalent regular loop is longer but follows the same steps.
numbers = [1, 2, 3, 4, 5]
squares = []
for number in numbers:
squares.append(number ** 2)
print(squares)The official Python tutorial on list comprehensions presents them as a concise way to create lists where each element is the result of an operation.
Transform strings
names = [" ada ", "GRACE", " guido"]
cleaned = [name.strip().title() for name in names]
print(cleaned)The expression can call methods and functions. Keep it focused on one understandable transformation. If it needs error handling, several temporary variables, or side effects, a normal loop is usually clearer.
Filter with an if clause
Place an if after the loop clause to include only matching items.
numbers = range(1, 21)
even_squares = [number ** 2 for number in numbers if number % 2 == 0]
print(even_squares)Read it from left to right: create number ** 2 for each number in the range if the number is even. The condition filters before the expression is collected.
Conditional expressions inside a comprehension
An inline if/else chooses between two output values and appears before the for. It transforms every input rather than removing items.
scores = [95, 72, 88, 61]
labels = ["pass" if score >= 70 else "fail" for score in scores]
print(labels)Compare the two roles: [x for x in values if condition] filters; [a if condition else b for x in values] maps every value. The guide to the Python conditional expression explains the inline form.
Use functions for complex transformations
A helper function keeps validation and branching testable while the comprehension describes collection.
def normalize_email(value: str) -> str:
email = value.strip().lower()
if "@" not in email:
raise ValueError(f"Invalid email: {value!r}")
return email
raw = [" [email protected] ", "[email protected]"]
emails = [normalize_email(value) for value in raw]
print(emails)This is preferable to packing several checks into the comprehension. It also works well with type hints and unit tests.
Nested comprehensions
A comprehension can contain multiple for clauses. Their order matches nested loops.
pairs = [
(letter, number)
for letter in ["A", "B"]
for number in [1, 2, 3]
]
print(pairs)Equivalent loops make the order explicit.
pairs = []
for letter in ["A", "B"]:
for number in [1, 2, 3]:
pairs.append((letter, number))One or two short clauses may be readable. Deeply nested comprehensions usually deserve named loops or helper functions.
Flatten a list of lists
matrix = [
[1, 2, 3],
[4, 5, 6],
]
flat = [value for row in matrix for value in row]
print(flat)This follows the same outer-then-inner loop order. For arbitrary nested structures, a recursive function or specialized iterator is more appropriate than adding more clauses.
Build a matrix
rows = 3
columns = 4
matrix = [
[0 for _ in range(columns)]
for _ in range(rows)
]
print(matrix)Each inner comprehension creates a distinct list. By contrast, [[0] * columns] * rows repeats references to the same inner list, so changing one row can change all rows.
Dictionary and set comprehensions
The same concept creates dictionaries and sets. A dictionary comprehension uses a key-value pair; a set comprehension automatically removes duplicates.
words = ["python", "api", "database"]
lengths = {word: len(word) for word in words}
initials = {word[0].upper() for word in words}
print(lengths)
print(initials)Learn more about these result types in the guides to dictionaries and sets.
List comprehension versus map and filter
Comprehensions are often easier to read when the transformation is a small Python expression. map() is attractive when an existing named function already describes the operation, and filter() may fit a reusable predicate.
values = ["1", "2", "3"]
with_comprehension = [int(value) for value in values]
with_map = list(map(int, values))
print(with_comprehension == with_map)The comparison in map and filter in Python discusses both styles. Prefer whichever communicates intent to your team.
List comprehension versus generator expression
Square brackets create the entire list immediately. Parentheses create a generator expression that yields values lazily.
squares_list = [number ** 2 for number in range(1_000)]
squares_generator = (number ** 2 for number in range(1_000))
print(type(squares_list).__name__)
print(type(squares_generator).__name__)Use a list when you need indexing, repeated iteration, length, or all values at once. Use a generator when values can be consumed once and the input may be large. The article on list comprehensions versus generator expressions explores memory and behavior in detail.
The walrus operator in a comprehension
An assignment expression can reuse a computed value, but it should improve rather than obscure readability.
strings = ["12", "bad", "30", "-4"]
positive = [
number
for text in strings
if text.lstrip("-").isdigit()
and (number := int(text)) > 0
]
print(positive)This avoids converting the same valid string twice, but a helper function may still be clearer in production. See the walrus operator guide before adopting the pattern broadly.
Avoid side effects
A comprehension should create a collection. Do not use it only to call print(), append elsewhere, write files, or update global state. A normal loop clearly communicates that actions—not a new list—are the purpose.
names = ["Maya", "Noah", "Liam"]
for name in names:
print(name)A practical data-cleaning example
records = [
{"name": " Ada ", "active": True, "score": "91"},
{"name": "Grace", "active": False, "score": "88"},
{"name": " Guido ", "active": True, "score": "95"},
]
active_users = [
{
"name": record["name"].strip(),
"score": int(record["score"]),
}
for record in records
if record["active"]
]
print(active_users)The comprehension is acceptable because the transformation and filter are still short. For CSV imports, missing keys, bad numbers, or optional values require a regular loop with explicit error handling. See working with CSV files for a complete pipeline.
Common mistakes
- Writing a comprehension so long that readers must mentally execute it.
- Confusing the filtering
ifwith an inlineif/elseexpression. - Using a comprehension only for side effects.
- Creating a huge list when a generator would be enough.
- Adding multiple nested loops and conditions without helper functions.
- Assuming a comprehension modifies the source list—it creates a new collection.
Frequently asked questions
Are comprehensions faster than loops?
They are often efficient because the iteration is implemented compactly, but performance depends on the work performed. Readability and correct memory behavior matter more than small timing differences in most application code.
Can I use try/except inside a comprehension?
Not as a statement. Put exception handling in a helper function or use a normal loop where rejected inputs can be reported clearly.
Can a comprehension create a tuple?
Parentheses around a comprehension create a generator expression, not a tuple. Wrap it with tuple(...) when an immutable tuple result is required.
Final thoughts
Python list comprehensions are ideal for one clear transformation with an optional simple filter. Extract complex logic into functions, switch to ordinary loops when actions or error reporting dominate, and choose a generator expression when materializing every result would waste memory. Concision is valuable only when the result remains immediately understandable.






