How to Master Python List Comprehensions and Generators.

BerylBeryl
5 min read

When you come across clean and elegant Python code, chances are high that it’s using list comprehensions or generator expressions. These aren’t just fancy one liners; they’re powerful tools that make your code shorter, faster, more expressive and pythonic.

If you've ever asked:

“What exactly is happening inside that one-liner?”
“Why do developers prefer these over normal loops?”
“How do I actually use them properly without confusion?”

Then this article is for you.

What Is a List Comprehension?

A list comprehension is a compact way to generate a new list by transforming items from an existing iterable(like a list, tuple or string).

Regular way of using a loop:

doubled = []
for x in range(5):
    doubled.apppend(x*2)

using list comprehension:

doubled = [x * 2 for x in range(5)]

why this matters:

It reads almost like natural language:

“I’m building a list by doubling each number from 0 to 4.”

This is what pythonic code entails; removing the boilerplate while keeping intent crystal clear.

List comprehension syntax ,fully explained:

[expression for item in iterable if condition]

Breakdown:

expression: what you want to do with the item(e.g., x * 2 )

item in iterable: the loop, just like a for loop.

if condition(optional):a filter that includes only items that pass the test.

Example: Squares of even numbers

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

what’s happening:

1.Loop through range(10)

2.keep only even numbers

3.square them

Result:

[0,4,16,36,64]

Conditional Logic; inline.

if…else

You can an use inline if…else expression inside a comprehension.

Example :Labelling numbers as “even” or “odd”

labels = ['even' if x % 2 == 0 else 'odd' for x in range(5)]

Output:

['even','odd','even','odd','even']

NOTE: The if…else expression goes before the for loop.

Nested list comprehensions.

List comprehensions can handle multiple loops which proves useful when dealing with 2D lists or combinations.

Example :Flatten a list of lists

matrix = [[1,2], [3,4], [5,6]]
flat = [num for row in matrix for num in row]

You’re saying:

“For each row in the matrix, get each number in the row”

Result:

[1,2,3,4,5,6]

This is equivalent to:

for row in matrix:
    for num in row:
        flat.append(num)

Generator Expressions.

Generator expressions in Python provide a concise way to create iterators, similar to list comprehensions but with lazy evaluation. They use parentheses () instead of brackets []
The difference?

List comprehensions build the entire list in memory while generator expressions yield items one at a time, on demand.

They’re ideal for:

Large datasets.

Streaming data.

One-time calculations.

Example: Summing a million numbers

total = sum(x for x in range(10**6))

Efficient: No list is stored. Memory usage stays low.

Set and dictionary comprehensions.

List style syntax also works for sets and dictionaries.

Set comprehension; remove duplicates automatically.

unique_squares = {x**2 for x in range(5)}

Result:

{0,1,4,9,16}

Dictionary comprehension; build key-value mappings

square_map = {x:x**2 for x in range(5)}

Result:

{0:0, 1:1, 2:4, 3:9, 4:16}

Mix with built-in tools.

These comprehensions play very well with python’s standard library.

zip() +List Comprehension.

names=['Alice', 'Bob']
scores=[90, 95]
report=[f"{name}:{score}" for name,score in zip(names, scores)]

enumerate() +List comprehension.

items=['a','b','c']
indexed=[(i,v) for i,v in enumerate(items)]

itertools. islice() +Generator.

from itertools import islice 
total=sum(islice((x for x in range(10**6)),100))

Performance: List vs Generator .

Let’s compare memory usage:

import sys

list_comp=[x for x in range(1000)]
gen_expr=(x for x in range(1000))

print(sys.getsizeof(list_comp)) #~9024 bytes
print(sys.getsizeof(gen_expr))  #~112 bytes

List comprehension stores all the values while generator expressions only store the logic.

Common pitfalls.

1.Misplaced if and else
Incorrect:

[x for x in nums if x>0 else 0] #syntaxError

Correct:

[x if x>0 else 0 for x in nums]

2.Reusing Generators.

Generators are single-use. After consumption, they’re done.

gen=(x for x in range(5))
list(gen) #[0,1,2,3,4]
list(gen) #[]

If you need it twice, convert to a list first.

Advanced Patterns.

Using the Walrus Operator (:=) for intermediate storage.

squares=[y for x in range(5) if (y := x**2) > 5]
#Output: [9,16]

This uses the walrus operator (:=), also called the assignment expression, to both compute and store a value (x**2) in a variable (y) within the if condition of the list comprehension. Without the walrus operator, you'd need to compute x**2 twice; once in the if clause and once in the result list:

squares = [x**2 for x in range(5) if x**2 > 5]  # less efficient

Using := stores the intermediate result (x**2) in y, avoiding redundant computation and improving readability when the expression is complex or used multiple times.

Nested comprehension for matrix transpose.

matrix=[[1,2,3],[4,5,6]]
transposed=[[row[i] for row in matrix] for i in range(3)]
  • Explanation:

    • We use a nested list comprehension to build the transposed matrix.

    • Outer loop (for i in range(3)):

      • Iterates over column indices (0 to 2) of the original matrix.

      • Each i represents a column we’re converting into a row.

    • Inner loop ([row[i] for row in matrix]):

      • For each i, it loops through all rows.

      • Picks the i-th element from each row (i.e., the column value).

    • The inner loop returns one full column as a list.

    • The outer loop wraps this in a new list, repeating for each column index.

  • Result:

      pythonCopyEdittransposed = [
          [1, 4],  # column 0 becomes row 0
          [2, 5],  # column 1 becomes row 1
          [3, 6]   # column 2 becomes row 2
      ]
    
  • Why it's useful:

    • Clean, Pythonic way to transpose a matrix without using external libraries.

    • Easy to scale with len(matrix[0]) for dynamic column count.

When to use what?

GoalUse
You need the result as a reusable listList Comprehension
You’re processing large data efficientlyGenerator Expression
You want to remove duplicatesSet Comprehension
You’re building key-value pairsDictionary Comprehension

When NOT to use comprehensions.

1.The logic is too complex or hard to follow.
2.You need multiple side-effects(e.g printing ,logging)
3.You’re debugging because loops are easier to step through.

When in doubt, opt for readability.

If you found this helpful; have your own insights on list comprehensions and generator expressions in python; would like to offer criticism or there’s a problematic python concept you would like me to cover: feel free to drop a comment below. Thank you.

0
Subscribe to my newsletter

Read articles from Beryl directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Beryl
Beryl

Data Science |Public Health |I am dedicated to developing my skills at the intersection of health and technology.