Python Tutorials

Choose a lesson to begin

List Comprehensions and Generator Expressions

List comprehensions are one of Python's most elegant features. They let you create new lists in a single, readable line. Generator expressions are their memory-efficient cousins.

Why List Comprehensions?

Old way (verbose):

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

New way (Pythonic):

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

Both create [0, 1, 4, 9, 16, 25, 36, 49, 64, 81], but the comprehension is cleaner and faster.

Basic Syntax

[expression for item in iterable]

Examples:

# Double each number
numbers = [1, 2, 3, 4, 5]
doubled = [n * 2 for n in numbers]
# [2, 4, 6, 8, 10]
# convert to uppercase
words = ["hello", "world"]
upper = [w.upper() for w in words]
# ['HELLO', 'WORLD']
# get lengths
words = ["cat", "dog", "elephant"]
lengths = [len(w) for w in words]
# [3, 3, 8]

With Conditions (Filtering)

Add an if clause to filter items:

[expression for item in iterable if condition]

Examples:

# Only even numbers
numbers = range(20)
evens = [n for n in numbers if n % 2 == 0]
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
# only long words
words = ["cat", "elephant", "dog", "rhinoceros"]
long_words = [w for w in words if len(w) > 5]
# ['elephant', 'rhinoceros']
# positive numbers only
values = [-5, 3, -2, 8, -1, 10]
positives = [v for v in values if v > 0]
# [3, 8, 10]

Transforming with Conditions

You can combine transformation and filtering:

# square only even numbers
numbers = range(10)
even_squares = [n**2 for n in numbers if n % 2 == 0]
# [0, 4, 16, 36, 64]
# uppercase only long words
words = ["cat", "python", "go", "javascript"]
upper_long = [w.upper() for w in words if len(w) > 3]
# ['PYTHON', 'JAVASCRIPT']

If-Else in Comprehensions

Use a ternary expression for conditional values:

[true_value if condition else false_value for item in iterable]

Examples:

# Label numbers as even/odd
numbers = range(5)
labels = ["even" if n % 2 == 0 else "odd" for n in numbers]
# ['even', 'odd', 'even', 'odd', 'even']
# cap values at maximum
values = [10, 50, 30, 80, 20]
capped = [v if v <= 50 else 50 for v in values]
# [10, 50, 30, 50, 20]
# replace negative with zero
numbers = [5, -3, 8, -1, 10]
positives = [n if n > 0 else 0 for n in numbers]
# [5, 0, 8, 0, 10]

Nested Comprehensions

Create multi-dimensional structures:

# multiplication table
matrix = [[i*j for j in range(1, 6)] for i in range(1, 6)]
for row in matrix:
    print(row)
# [1, 2, 3, 4, 5]
# [2, 4, 6, 8, 10]
# [3, 6, 9, 12, 15]
# [4, 8, 12, 16, 20]
# [5, 10, 15, 20, 25]
# flatten a nested list
nested = [[1, 2], [3, 4], [5, 6]]
flat = [num for sublist in nested for num in sublist]
# [1, 2, 3, 4, 5, 6]
# create coordinate pairs
coords = [(x, y) for x in range(3) for y in range(3)]
# [(0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2)]

Set and Dictionary Comprehensions

Same syntax works for sets and dicts:

# set comprehension (unique values)
numbers = [1, 2, 2, 3, 3, 3, 4]
unique_squares = {n**2 for n in numbers}
# {1, 4, 9, 16}
# dictionary comprehension
words = ["cat", "dog", "elephant"]
word_lengths = {w: len(w) for w in words}
# {'cat': 3, 'dog': 3, 'elephant': 8}
# 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'}

Generator Expressions

Use () instead of [] to create a generator:

# list comprehension (creates entire list in memory)
squares_list = [x**2 for x in range(1000000)]  # Uses lots of memory!
# generator expression (computes on demand)
squares_gen = (x**2 for x in range(1000000))   # Minimal memory!

Why generators?

Using generators:
# create generator
gen = (x**2 for x in range(10))
# get values one at a time
print(next(gen))  # 0
print(next(gen))  # 1
print(next(gen))  # 4
# or use in a loop
gen = (x**2 for x in range(10))
for square in gen:
    print(square)
# or convert to list when needed
gen = (x**2 for x in range(10))
all_squares = list(gen)

When to Use Generators

Good uses:

# sum large sequence (doesn't need to store all values)
total = sum(x**2 for x in range(1000000))
# check if any value matches (stops at first match)
has_big = any(x > 100 for x in range(1000))
# chain operations without intermediate lists
processed = (
    x * 2 
    for x in range(1000) 
    if x % 2 == 0
)

When to use lists instead:

Real-World Examples

Parse and Filter Data

# extract valid emails
lines = ["user@example.com", "invalid", "admin@site.org", "bad@"]
emails = [line for line in lines if "@" in line and "." in line]

Data Transformation

# clean and format user input
inputs = ["  Alice  ", "BOB", "charlie  "]
cleaned = [name.strip().title() for name in inputs]
# ['Alice', 'Bob', 'Charlie']

Matrix Operations

# 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]]

File Processing

# process log file (generator for memory efficiency)
with open("huge_log.txt") as f:
    errors = (line for line in f if "ERROR" in line)
    for error in errors:
        print(error.strip())

Performance Comparison

import time
# list comprehension (stores all)
start = time.time()
squares_list = [x**2 for x in range(1000000)]
total = sum(squares_list)
print(f"List: {time.time() - start:.4f}s")
# generator (computes on demand)
start = time.time()
total = sum(x**2 for x in range(1000000))
print(f"Generator: {time.time() - start:.4f}s")

Generators are often faster and always use less memory!

Common Patterns

Extract specific fields:

users = [
    {"name": "Alice", "age": 30},
    {"name": "Bob", "age": 25}
]
names = [u["name"] for u in users]
# ['Alice', 'Bob']

Filter and transform:

prices = [10.5, 20.0, 5.5, 30.0]
discounted = [p * 0.9 for p in prices if p > 10]
# [18.9, 27.0]

Combine multiple lists:

names = ["Alice", "Bob", "Charlie"]
scores = [95, 87, 92]
combined = [(n, s) for n, s in zip(names, scores)]
# [('Alice', 95), ('Bob', 87), ('Charlie', 92)]

When NOT to Use Comprehensions

Too complex:

# bad - hard to read
result = [x*y for x in range(10) if x % 2 == 0 
          for y in range(10) if y % 3 == 0 
          if x + y < 15]
# better - use regular loop with comments
result = []
for x in range(10):
    if x % 2 != 0:
        continue
    for y in range(10):
        if y % 3 != 0:
            continue
        if x + y >= 15:
            continue
        result.append(x * y)

Need to handle errors:

# bad - can't easily catch exceptions
values = [int(x) for x in inputs]  # Crashes on non-numbers
# better
values = []
for x in inputs:
    try:
        values.append(int(x))
    except ValueError:
        print(f"Skipping invalid: {x}")

Try It Out!

  • Create a list of squares for numbers 1-20
  • Get all words longer than 4 characters from a sentence
  • Create a dictionary mapping numbers 1-10 to their cubes
  • Flatten a 3-level nested list
  • Use a generator to sum squares of even numbers 1-1000000
  • Key Takeaways

    Mastering comprehensions makes your Python code more elegant and Pythonic! 🐍

    🐍 Python Runner