Python Comprehensions — Write Cleaner, Faster Code

Sanjeev SharmaSanjeev Sharma
5 min read

Advertisement

Introduction

Why This Matters

Python comprehensions are a hallmark of idiomatic, "Pythonic" code. They allow you to create collections — lists, dicts, sets, or lazy generators — in a single readable line, replacing verbose for loops with a more declarative style. Every experienced Python developer uses them daily, and they appear in data pipelines, API transformations, and configuration handling across frameworks like Django, FastAPI, and pandas.

Beyond readability, comprehensions have a performance edge. Python's interpreter can optimize list comprehensions through internal C-level loops, making them meaningfully faster than equivalent for loops with .append() calls. Understanding when to use comprehensions versus standard loops is one of the distinguishing skills of a professional Python developer.

Comprehensions also integrate naturally with type hints, functional tools like map() and filter(), and libraries like NumPy and pandas. This guide covers every form with real-world examples.

List Comprehensions

The most common form: [expression for item in iterable if condition].

# Basic: square every number
squares = [x ** 2 for x in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
 
# With filter: only even numbers
even_squares = [x ** 2 for x in range(10) if x % 2 == 0]
# [0, 4, 16, 36, 64]
 
# Transform strings
names = ["alice", "bob", "charlie"]
capitalized = [name.capitalize() for name in names]
# ['Alice', 'Bob', 'Charlie']
 
# Flatten nested list
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [num for row in matrix for num in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

Dictionary Comprehensions

# 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'}
 
# Filter by value
scores = {"Alice": 85, "Bob": 60, "Charlie": 92}
passed = {name: score for name, score in scores.items() if score >= 70}
# {'Alice': 85, 'Charlie': 92}
 
# Build lookup from list of objects
users = [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]
user_map = {u["id"]: u["name"] for u in users}
# {1: 'Alice', 2: 'Bob'}

Set Comprehensions

# Unique characters in a string
text = "mississippi"
unique_chars = {char for char in text}
# {'m', 'i', 's', 'p'}
 
# Unique domains from email list
emails = ["alice@gmail.com", "bob@yahoo.com", "charlie@gmail.com"]
domains = {email.split("@")[1] for email in emails}
# {'gmail.com', 'yahoo.com'}

Generator Expressions

Generators produce items lazily — one at a time — making them ideal for large datasets where you don't need all values in memory simultaneously.

# Sum of squares without building a list
total = sum(x ** 2 for x in range(1_000_000))
 
# Read large file line by line
def read_non_empty(path: str):
    with open(path) as f:
        return (line.strip() for line in f if line.strip())
 
# Chain with next() for first match
first_even = next(x for x in range(100) if x % 2 == 0 and x > 10)
# 12

Nested Comprehensions

Use carefully — deep nesting harms readability.

# Transpose a matrix
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transposed = [[row[i] for row in matrix] for i in range(3)]
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
 
# Cartesian product
colors = ["red", "blue"]
sizes = ["S", "M", "L"]
variants = [(color, size) for color in colors for size in sizes]

Comprehension vs map/filter

numbers = range(10)
 
# map/filter style (less readable)
result_map = list(map(lambda x: x ** 2, filter(lambda x: x % 2 == 0, numbers)))
 
# Comprehension style (preferred)
result_comp = [x ** 2 for x in numbers if x % 2 == 0]
 
# Both produce: [0, 4, 16, 36, 64]

Common Mistakes

  • Using a list comprehension when a generator expression would suffice for large data
  • Nesting comprehensions more than two levels — use a helper function instead
  • Putting side effects (like print()) inside comprehensions — use a regular loop
  • Overusing comprehensions to show off instead of writing readable code
  • Forgetting that dict comprehensions overwrite duplicate keys silently

Best Practices

  • Use list comprehensions for building small-to-medium collections from iterables
  • Use generator expressions when iterating once or when memory matters
  • Limit comprehensions to a single logical condition; split complex logic into functions
  • Prefer comprehensions over map() and filter() for readability in most cases
  • Always test comprehension output with small inputs before running on large datasets

Key Takeaways

  • List comprehensions replace for loops with .append() and are faster due to C-level optimization
  • Dict comprehensions create dictionaries from key-value expressions in one line
  • Set comprehensions automatically deduplicate values
  • Generator expressions are lazy and memory-efficient — use them for large sequences
  • The syntax is [expr for item in iterable if condition] — the if clause is optional
  • Nested comprehensions are allowed but should be limited to two levels for readability
  • Comprehensions are Pythonic — they signal experience and code quality to reviewers

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro

Related reading