🚀 Supercharge your YouTube channel's growth with AI.
Try YTGrowAI FreePython List Comprehension: The Complete Guide with Examples
When I first saw a list comprehension in Python, I thought it was some advanced wizardry. Turns out, it is just a cleaner way to build lists, and once you understand the pattern, you will never go back to writing verbose loops. In this guide, I will break down list comprehension from the ground up so you can use it confidently in your own code.
What Is List Comprehension in Python?
List comprehension is a concise syntax in Python for creating a new list from an existing iterable. Instead of writing a multi-line for loop and appending items one by one, you can build the entire list in a single line of code. The syntax reads almost like a sentence in plain English, which makes it easy to understand at a glance.
The basic pattern looks like this:
new_list = [expression for item in iterable]
The expression determines what each item looks like in the new list. The for loop iterates over the source iterable. The result is a brand new list that you can store, return, or manipulate further.
Basic List Comprehension Syntax
Let me start with the simplest possible example. Suppose you have a list of numbers and you want to create a new list where each number is doubled.
numbers = [1, 2, 3, 4, 5] doubled = [x * 2 for x in numbers] print(doubled)
The output is:
[2, 4, 6, 8, 10]
Here is what happened step by step. The for loop pulled each value from the numbers list one at a time. The expression x * 2 ran on each value. The results collected into a new list. That is the entire mechanism.
You can also transform strings. Here is how you create a list of word lengths from a list of words.
words = ["apple", "banana", "cherry", "date"] lengths = [len(word) for word in words] print(lengths)
The output:
[5, 6, 6, 4]
The expression can be any valid Python expression. You can call functions, run arithmetic, format strings, or combine multiple values.
prices = [100, 250, 89, 340] discounted = [price * 0.9 for price in prices] print(discounted)
Each price gets reduced by 10 percent. The output shows the discounted values rounded to two decimal places:
[90.0, 225.0, 80.1, 306.0]
Adding Conditions to List Comprehension
The real power of list comprehension shows up when you add filtering logic. You can include an if clause after the for part to select only certain items from the source. This eliminates the need for a separate if block inside the loop.
Here is how you keep only the even numbers from a list:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] evens = [x for x in numbers if x % 2 == 0] print(evens)
Output:
[2, 4, 6, 8, 10]
The if clause filters out odd numbers before they reach the expression stage. Only values that satisfy the condition make it into the final list.
Here is a practical example with strings. Suppose you want to collect only the names that start with the letter A from a list of customer names.
customers = ["Alice", "Bob", "Amanda", "Charlie", "Adrian", "Eve"]
a_names = [name for name in customers if name.startswith("A")]
print(a_names)
Output:
["Alice", "Amanda", "Adrian"]
String methods like startswith() work perfectly inside the condition. You can also use more complex boolean expressions.
Here is an example that filters numbers greater than a threshold:
temperatures = [22, 19, 28, 31, 25, 18, 30, 35] hot_days = [temp for temp in temperatures if temp > 27] print(hot_days)
Output:
[28, 31, 30, 35]
Using If Else in List Comprehension
Sometimes you need to apply different transformations based on a condition. List comprehension supports an optional else branch on the left side of the expression. This is different from the trailing if clause, which only filters items. Here the else determines what value appears in the result list for each item.
The syntax with if else follows this pattern:
new_list = [expression_if_true if condition else expression_if_false for item in iterable]
Here is a classification example. Suppose you have a list of scores and you want to label each score as “Pass” or “Fail” based on a passing threshold of 40.
scores = [45, 32, 78, 22, 55, 18, 90, 41] labels = ["Pass" if score >= 40 else "Fail" for score in scores] print(labels)
Output:
["Pass", "Fail", "Pass", "Fail", "Pass", "Fail", "Pass", "Pass"]
Every score gets evaluated. The ternary expression on the left decides which label appears in the output list. There is no filtering here, every source item produces exactly one output item.
You can also combine numeric transformations with conditional logic. Here is an example that adjusts prices: items under 100 get a 20 percent discount while items at 100 or above get a 10 percent discount.
prices = [45, 120, 89, 200, 75] adjusted = [price * 0.8 if price < 100 else price * 0.9 for price in prices] print(adjusted)
Output:
[36.0, 108.0, 71.2, 180.0, 60.0]
Multiple Conditions in List Comprehension
You can stack multiple if conditions in a row. Each condition acts as a filter, and only items that satisfy all of them reach the expression stage.
numbers = [x for x in range(1, 21) if x % 2 == 0 if x % 3 == 0] print(numbers)
Output:
[6, 12, 18]
This found numbers divisible by both 2 and 3 in the range 1 through 20. The number 6 satisfies both conditions, so it appears in the result. The number 12 and 18 also qualify. Numbers like 4 or 8 fail the second condition and are excluded.
You can also mix and match if and if else constructs in more complex scenarios.
Nested List Comprehension
List comprehensions can be nested inside each other. This is useful when you need to transform data that has multiple levels of structure, like a matrix or a list of lists. The outer loop processes the outer list, and the inner loop processes each sub-list.
Here is how you flatten a 2D list:
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]
Read this from right to left to understand the nesting. The inner loop for num in row extracts individual numbers from each sub-list. The outer loop for row in matrix moves through each sub-list. The expression num feeds each extracted value into the final list.
You can also perform operations on nested structures. Here is how you compute a running total from a list of weekly sales where each week is a list of daily sales figures:
weekly_sales = [
[120, 150, 130],
[200, 180, 220],
[90, 110, 100]
]
total_per_week = [sum(week) for week in weekly_sales]
print(total_per_week)
Output:
[400, 600, 300]
The inner sum() call adds up daily sales for each week. The list comprehension produces a single list of weekly totals.
Here is a more advanced example that extracts values from a nested dictionary structure:
data = [
{"name": "Alice", "scores": [85, 92, 78]},
{"name": "Bob", "scores": [90, 81, 88]},
{"name": "Charlie", "scores": [72, 95, 83]}
]
avg_scores = [sum(person["scores"]) / len(person["scores"]) for person in data]
print(avg_scores)
Output:
[85.0, 86.33333333333333, 83.33333333333333]
Each person's scores are extracted, averaged, and added to the result list. Nested data structures are common in real Python projects, so this pattern is genuinely useful.
List Comprehension vs Traditional Loops
Let me show you the direct comparison. The following two pieces of code produce identical results, but the list comprehension version is significantly shorter.
Traditional loop approach:
numbers = [1, 2, 3, 4, 5]
squares = []
for num in numbers:
squares.append(num ** 2)
print(squares)
List comprehension approach:
numbers = [1, 2, 3, 4, 5] squares = [num ** 2 for num in numbers] print(squares)
Both produce [1, 4, 9, 16, 25]. The list comprehension version reduces five lines to one. For simple transformations, this compactness is a genuine advantage.
There is also a performance difference in most cases. List comprehension is implemented in C under the hood for the interpreter, so it often runs faster than an equivalent for loop with append calls. The difference is small for most everyday scripts, but it becomes noticeable when processing large datasets.
Transforming and Filtering at the Same Time
You can combine the trailing if with the main expression to both filter and transform in a single comprehension. This is one of the most common patterns in practical Python code.
Suppose you have a list of product prices and you want the final prices after tax, but only for products that cost at least 50 rupees.
prices = [45, 120, 89, 200, 30, 75, 150] tax_rate = 1.18 final_prices = [price * tax_rate for price in prices if price >= 50] print(final_prices)
Output:
[141.6, 105.02, 236.0, 88.5, 177.0]
The items priced at 45 and 30 are filtered out. The remaining items are multiplied by the tax rate. The transformation and filtering happen in one comprehension without any extra variables or intermediate steps.
Here is an example that cleans and transforms raw user data. You have a list of usernames, some of which have leading or trailing whitespace, and you want to normalize them to lowercase and remove the whitespace.
raw_usernames = [" alice ", "BOB", " charlie ", "Diana", " eve "] clean_usernames = [username.strip().lower() for username in raw_usernames] print(clean_usernames)
Output:
["alice", "bob", "charlie", "diana", "eve"]
Method chaining inside the expression makes this clean and readable. Each username goes through strip() to remove whitespace and then lower() to normalize the case.
Working with Ranges
List comprehension pairs naturally with Python's range() function. You can generate sequences of numbers directly inside the comprehension without building a separate list first.
squares_of_even = [x ** 2 for x in range(1, 11) if x % 2 == 0] print(squares_of_even)
Output:
[4, 16, 36, 64, 100]
The range starts at 1 and ends at 10. The even numbers are 2, 4, 6, 8, and 10. Each gets squared. The odd numbers are filtered out before any transformation occurs.
You can generate more complex sequences too. Here is how you create a list of powers of two up to a specified limit:
limit = 10 powers_of_two = [2 ** x for x in range(limit)] print(powers_of_two)
Output:
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
The range goes from 0 to limit minus 1, so x takes values 0 through 9. The expression 2 ** x produces the powers of two from 2 to the 9th power.
List Comprehension with zip and enumerate
Sometimes you need the index alongside the value. Python provides zip and enumerate for this, and both work seamlessly inside list comprehensions.
Here is how you combine two lists element by element:
names = ["Alice", "Bob", "Charlie"]
scores = [92, 85, 78]
combined = [f"{name}: {score}" for name, score in zip(names, scores)]
print(combined)
Output:
["Alice: 92", "Bob: 85", "Charlie: 78"]
The zip() function pairs corresponding elements from both lists. The comprehension builds a formatted string for each pair.
Here is how you use enumerate to get both index and value:
fruits = ["apple", "banana", "cherry", "date"]
indexed = [f"{i}: {fruit}" for i, fruit in enumerate(fruits)]
print(indexed)
Output:
["0: apple", "1: banana", "2: cherry", "3: date"]
The enumerate function wraps the original iterable and yields tuples of (index, value). The comprehension unpacks each tuple and builds the formatted string.
List Comprehension with Dictionary and Set
List comprehension is specifically for lists, but Python has similar syntax for dictionaries and sets. Understanding these variants helps you write consistent code across different data structures.
Dictionary comprehension uses curly braces and key-value expressions:
numbers = [1, 2, 3, 4, 5]
squares_dict = {x: x ** 2 for x in numbers}
print(squares_dict)
Output:
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
Set comprehension uses curly braces without key-value pairs:
numbers = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
unique_squares = {x ** 2 for x in numbers}
print(unique_squares)
Output:
{1, 4, 9, 16}
The duplicates in the original list are automatically removed by the set. Each unique number gets squared, and the result is a set of unique squares.
Common Mistakes to Avoid
List comprehension is powerful, but there are a few traps that catch many developers, especially when they are starting out.
The first mistake is creating unintended side effects inside the expression. Because the expression runs for every item, avoid writing code that mutates shared state or performs I/O operations inside the comprehension. Keep the expression side effects free.
# This works but is a bad pattern results = [print(x) or x for x in [1, 2, 3]]
This prints values during iteration and also collects them into a list. It works, but it is confusing to read and generally considered poor practice. Use a regular for loop if you need side effects.
The second mistake is forgetting the square brackets entirely. List comprehension requires the outer brackets. Without them, Python interprets the for expression as a generator expression, which behaves differently.
# This creates a generator, not a list result = (x * 2 for x in range(5)) print(result)
Output:
<generator object at 0x...>
The output shows a generator object, not a list of doubled numbers. You need square brackets for a list comprehension.
# This is the correct list comprehension result = [x * 2 for x in range(5)] print(result)
Output:
[0, 2, 4, 6, 8]
The third mistake is writing overly complex comprehensions that span multiple lines and include too much logic. List comprehension is meant to improve readability, not to squeeze an entire algorithm into one line. If your comprehension becomes hard to read, split it into a regular loop or break it into multiple steps.
Performance Considerations
List comprehension is generally faster than using append inside a loop. The CPython implementation handles the iteration and storage more efficiently in comprehension form. For small to medium sized lists, the difference is negligible, but on large datasets the performance gap can be measurable.
Here is a quick benchmark comparison:
import time
# Traditional loop
start = time.perf_counter()
result_loop = []
for i in range(10000):
result_loop.append(i * 2)
loop_time = time.perf_counter() - start
# List comprehension
start = time.perf_counter()
result_comp = [i * 2 for i in range(10000)]
comp_time = time.perf_counter() - start
print(f"Loop time: {loop_time:.4f}")
print(f"Comprehension time: {comp_time:.4f}")
print(f"Comprehension is {loop_time / comp_time:.2f}x faster")
On most machines, the comprehension version completes in roughly half the time of the loop version for 10,000 iterations. The gap widens as the dataset grows. This is one of those practical advantages that accumulates over many runs.
Practical Examples
Let me walk through a few scenarios where list comprehension genuinely solves everyday problems better than alternatives.
Example 1: Extracting specific data from a list of dictionaries. You have a list of employee records and you want to extract just the email addresses for those who have the "Engineering" department.
employees = [
{"name": "Alice", "dept": "Engineering", "email": "[email protected]"},
{"name": "Bob", "dept": "Marketing", "email": "[email protected]"},
{"name": "Charlie", "dept": "Engineering", "email": "[email protected]"},
{"name": "Diana", "dept": "Sales", "email": "[email protected]"}
]
eng_emails = [emp["email"] for emp in employees if emp["dept"] == "Engineering"]
print(eng_emails)
Output:
["[email protected]", "[email protected]"]
Example 2: Normalizing a list of user inputs. You receive a list of phone numbers as strings with various formatting characters, and you want to extract only the digits.
raw_numbers = ["+91-98765-43210", "9876543210", "98-765-432-10", "+91 98765 43210"] clean_numbers = ["".join(c for c in num if c.isdigit()) for num in raw_numbers] print(clean_numbers)
Output:
["919876543210", "9876543210", "9876543210", "919876543210"]
The inner comprehension iterates over each character, keeping only digit characters. The outer comprehension processes each raw phone number string. The result is a list of clean numeric strings.
Example 3: Converting a list of temperatures from Celsius to Fahrenheit, but only for values within a valid range.
celsius = [-10, 0, 15, 22, 37, 100, 250] fahrenheit = [(c * 9/5) + 32 for c in celsius if -40 <= c <= 100] print(fahrenheit)
Output:
[14.0, 32.0, 59.0, 71.6, 98.6, 212.0]
The values -10, 250 fall outside the valid range and are filtered out. The remaining values get converted.
When Not to Use List Comprehension
List comprehension is not always the right tool. There are situations where a regular loop produces clearer code.
When the transformation logic is complex and spans multiple lines, a loop with clear variable names and comments is easier to maintain. List comprehension rewards simple, straightforward transformations. Anything that requires extensive debugging or tracing should probably live in a regular loop.
When you need to break early from the iteration or skip items in a non-trivial pattern, a loop gives you more control. List comprehension processes every item in the source unless you add filtering, and you cannot use break or continue statements inside a comprehension.
When the comprehension would exceed 80 characters or involve deeply nested logic, readability suffers. Aim for comprehensions that fit comfortably on one or two lines.
Frequently Asked Questions
Can list comprehension handle multiple for loops?
Yes. You can chain multiple for clauses in a single comprehension. Each subsequent for iterates over the result of the previous one. This works for flattening nested structures or building Cartesian products.
pairs = [(x, y) for x in [1, 2] for y in ['a', 'b']] print(pairs)
Output:
[(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]
Can I use functions inside list comprehension?
Absolutely. Any callable that returns a value works in the expression part. You can pass each item through a function to transform it.
def classify(score):
if score >= 75:
return "Distinction"
elif score >= 60:
return "First Class"
else:
return "Pass"
scores = [82, 45, 71, 90, 55]
grades = [classify(s) for s in scores]
print(grades)
Output:
["Distinction", "Pass", "First Class", "Distinction", "Pass"]
Does list comprehension work with strings?
Yes. Strings are iterables in Python, so you can iterate over them character by character in a comprehension.
word = "python" upper_vowels = [c.upper() for c in word if c in "aeiou"] print(upper_vowels)
Output:
['O', 'O']
What is the difference between list comprehension and generator expression?
List comprehension uses square brackets and creates the entire list in memory immediately. Generator expression uses parentheses and produces items lazily, one at a time as you iterate. Generator expressions are more memory efficient for large sequences but cannot be indexed or reused after iteration completes.
list_comp = [x * 2 for x in range(3)] gen_exp = (x * 2 for x in range(3)) print(list_comp) print(list(gen_exp))
Output:
[0, 2, 4] [0, 2, 4]
How do I convert a list comprehension result back to a string?
Use the join method with str() to convert each item to a string, then join them with a delimiter.
numbers = [1, 2, 3, 4, 5] result = ", ".join(str(n) for n in numbers) print(result)
Output:
1, 2, 3, 4, 5
Note that the inner expression uses a generator expression rather than a list comprehension, which is more memory efficient for string joining operations.
Summary
List comprehension is one of those Python features that changes how you write code. It lets you build lists from iterables in a single expressive line, replacing multi-line loops with clear, compact syntax. You can add filtering conditions, conditional transformations, and nested structures to handle a wide variety of scenarios.
The pattern works with any iterable: lists, strings, ranges, dictionaries, and more. For transformations that fit a single line, list comprehension is faster and more readable than an equivalent loop. For complex logic that spans multiple steps, a regular loop is the better choice.
Start using list comprehension for simple transformations in your daily code. Once you are comfortable with the basic pattern, introduce filtering conditions, then conditional expressions, and finally nested comprehensions. By the end, you will reach for this tool naturally whenever you need to build or transform a list.