You want sum(x**2 for x in range(1000000)). Creating a list of a million squares would waste memory. Generator expressions use parentheses instead of brackets and create values on demand - perfect for aggregation functions.

Basic syntax

Use parentheses instead of brackets.

basic.py
# Basic Generator Expression

print("=== Generator Expression vs List Comprehension ===\n")

# List comprehension: square brackets
limit = 
list_comp = [x ** 2 for x in range(limit)]
print(f"List comprehension: {list_comp}")
print(f"Type: {type(list_comp)}")

# Generator expression: parentheses
gen_exp = (x ** 2 for x in range(limit))
print(f"\nGenerator expression: {gen_exp}")
print(f"Type: {type(gen_exp)}")

print("\n=== Getting Values ===")

# List is ready to use
print(f"List[0]: {list_comp[0]}")
print(f"List length: {len(list_comp)}")

# Generator must be iterated
print("\nGenerator values:")
for val in gen_exp:
    print(f"  {val}")

print("\n=== One-Time Use ===")

gen = (x for x in range(3))

print("First iteration:")
print(f"  {list(gen)}")  # [0, 1, 2]

print("Second iteration:")
print(f"  {list(gen)}")  # [] - Empty! Already exhausted

print("\n=== Syntax Comparison ===")
print("""
List:       [expression for item in iterable]
Generator:  (expression for item in iterable)
            ^                                ^
            Parentheses instead of brackets!
""")

# Both support conditions
list_even = [x for x in range(10) if x % 2 == 0]
gen_even = (x for x in range(10) if x % 2 == 0)

print(f"List (evens): {list_even}")
print(f"Generator (evens): {list(gen_even)}")
# Basic Generator Expression

print("=== Generator Expression vs List Comprehension ===\n")

# List comprehension: square brackets
limit = 
list_comp = [x ** 2 for x in range(limit)]
print(f"List comprehension: {list_comp}")
print(f"Type: {type(list_comp)}")

# Generator expression: parentheses
gen_exp = (x ** 2 for x in range(limit))
print(f"\nGenerator expression: {gen_exp}")
print(f"Type: {type(gen_exp)}")

print("\n=== Getting Values ===")

# List is ready to use
print(f"List[0]: {list_comp[0]}")
print(f"List length: {len(list_comp)}")

# Generator must be iterated
print("\nGenerator values:")
for val in gen_exp:
    print(f"  {val}")

print("\n=== One-Time Use ===")

gen = (x for x in range(3))

print("First iteration:")
print(f"  {list(gen)}")  # [0, 1, 2]

print("Second iteration:")
print(f"  {list(gen)}")  # [] - Empty! Already exhausted

print("\n=== Syntax Comparison ===")
print("""
List:       [expression for item in iterable]
Generator:  (expression for item in iterable)
            ^                                ^
            Parentheses instead of brackets!
""")

# Both support conditions
list_even = [x for x in range(10) if x % 2 == 0]
gen_even = (x for x in range(10) if x % 2 == 0)

print(f"List (evens): {list_even}")
print(f"Generator (evens): {list(gen_even)}")
# Basic Generator Expression

print("=== Generator Expression vs List Comprehension ===\n")

# List comprehension: square brackets
limit = 
list_comp = [x ** 2 for x in range(limit)]
print(f"List comprehension: {list_comp}")
print(f"Type: {type(list_comp)}")

# Generator expression: parentheses
gen_exp = (x ** 2 for x in range(limit))
print(f"\nGenerator expression: {gen_exp}")
print(f"Type: {type(gen_exp)}")

print("\n=== Getting Values ===")

# List is ready to use
print(f"List[0]: {list_comp[0]}")
print(f"List length: {len(list_comp)}")

# Generator must be iterated
print("\nGenerator values:")
for val in gen_exp:
    print(f"  {val}")

print("\n=== One-Time Use ===")

gen = (x for x in range(3))

print("First iteration:")
print(f"  {list(gen)}")  # [0, 1, 2]

print("Second iteration:")
print(f"  {list(gen)}")  # [] - Empty! Already exhausted

print("\n=== Syntax Comparison ===")
print("""
List:       [expression for item in iterable]
Generator:  (expression for item in iterable)
            ^                                ^
            Parentheses instead of brackets!
""")

# Both support conditions
list_even = [x for x in range(10) if x % 2 == 0]
gen_even = (x for x in range(10) if x % 2 == 0)

print(f"List (evens): {list_even}")
print(f"Generator (evens): {list(gen_even)}")

(expression for item in iterable) - same as list comprehension but lazy.

generator expression Inline generator: `(x for x in items)`. Like list comprehension but memory-efficient.

Memory efficiency

Compare memory: list comprehension vs generator expression.

memory.py
# Memory Efficiency

import sys

print("=== Memory Comparison ===\n")

n = 10000  # Ten thousand numbers

# List comprehension
list_squares = [x ** 2 for x in range(n)]
list_size = sys.getsizeof(list_squares)

# Generator expression
gen_squares = (x ** 2 for x in range(n))
gen_size = sys.getsizeof(gen_squares)

print(f"Squares of 0 to {n-1}:")
print(f"  List: {list_size:,} bytes")
print(f"  Generator: {gen_size:,} bytes")
print(f"  Ratio: List is {list_size / gen_size:.0f}x larger!")

print("\n=== Why The Difference? ===")

print("""
List comprehension:
  [0, 1, 4, 9, 16, 25, ..., 99980001]
  → Stores ALL 10,000 squared values
  → Each integer takes memory
  → Plus list structure overhead

Generator expression:
  → Stores only the "recipe" to compute values
  → Computes each value when requested
  → Memory constant regardless of size
""")

print("=== Scaling Comparison ===\n")

sizes = [100, 1000, 10000, 100000]

for size in sizes:
    list_obj = [x for x in range(size)]
    gen_obj = (x for x in range(size))
    
    list_bytes = sys.getsizeof(list_obj)
    gen_bytes = sys.getsizeof(gen_obj)
    
    print(f"Size {size:>6,}: List {list_bytes:>8,} bytes, Gen {gen_bytes:>4} bytes")

print("\nNotice: Generator size stays constant!")

print("\n=== When Memory Matters ===")

# Processing large data
def process_large_dataset():
    # BAD: Creates huge list in memory
    # results = [expensive_operation(x) for x in huge_dataset]
    
    # GOOD: Processes one at a time
    # results = (expensive_operation(x) for x in huge_dataset)
    pass

print("""
Use generator expressions when:
  ✓ Processing large datasets
  ✓ You only iterate once
  ✓ Memory is constrained
  ✓ Early termination is possible

Use list comprehensions when:
  ✓ You need random access (result[5])
  ✓ You need to iterate multiple times
  ✓ You need len() 
  ✓ Dataset is small
""")

List stores all values. Generator expression produces one at a time.

With built-in functions

Generator expressions work great with sum, any, all, max, min.

builtin_functions.py
# Generator Expressions with Built-in Functions

print("=== sum() with Generator Expression ===\n")

# Sum of squares 1-100
# List way (creates intermediate list)
list_sum = sum([x ** 2 for x in range(1, 101)])
print(f"List way: sum([...]) = {list_sum}")

# Generator way (no intermediate list!)
gen_sum = sum(x ** 2 for x in range(1, 101))
print(f"Generator way: sum(...) = {gen_sum}")

print("\nNote: Can omit outer () when only argument!")

print("\n=== any() and all() ===")

numbers = [2, 4, 6, 7, 8, 10]
print(f"Numbers: {numbers}")

# any(): True if at least one True
has_odd = any(n % 2 != 0 for n in numbers)
print(f"any(n % 2 != 0): {has_odd}")  # True (7 is odd)

# all(): True if all are True
all_positive = all(n > 0 for n in numbers)
print(f"all(n > 0): {all_positive}")  # True

all_even = all(n % 2 == 0 for n in numbers)
print(f"all(n % 2 == 0): {all_even}")  # False (7 is odd)

print("\n=== Short-Circuit Evaluation ===")

def check(n):
    print(f"  Checking {n}...")
    return n > 5

data = [1, 2, 3, 6, 7, 8]
print(f"Data: {data}")

print("\nany(n > 5 for n in data):")
result = any(check(n) for n in data)
print(f"Result: {result}")
print("Stopped at 6! Didn't check 7 or 8.")

print("\n=== max() and min() ===")

words = ["apple", "pie", "extraordinary", "hi"]
print(f"Words: {words}")

longest = max(len(w) for w in words)
shortest = min(len(w) for w in words)
print(f"Longest word length: {longest}")
print(f"Shortest word length: {shortest}")

# Finding the actual word
longest_word = max(words, key=len)
print(f"Longest word: '{longest_word}'")

print("\n=== join() with Generator ===")

numbers = [1, 2, 3, 4, 5]
# Convert to strings and join
result = ', '.join(str(n) for n in numbers)
print(f"Joined: {result}")

sum(x**2 for x in items) - no intermediate list needed.

aggregation Combine values into one: `sum()`, `any()`, `all()`, `max()`, `min()`.

Iterate over generators

Use in for loops or convert to list when needed.

iteration.py
# Iterating Over Generator Expressions

print("=== for Loop ===\n")

# Basic iteration
gen = (x ** 2 for x in range(5))

print("Squares (for loop):")
for square in gen:
    print(f"  {square}")

print("\n=== Converting to Collections ===")

# To list
gen1 = (x * 2 for x in range(5))
as_list = list(gen1)
print(f"list(): {as_list}")

# To tuple
gen2 = (x * 2 for x in range(5))
as_tuple = tuple(gen2)
print(f"tuple(): {as_tuple}")

# To set
gen3 = (x % 3 for x in range(10))
as_set = set(gen3)
print(f"set(x % 3 for x in range(10)): {as_set}")

print("\n=== Manual Iteration with next() ===")

gen = (x ** 2 for x in range(3))

print("Using next():")
print(f"  next(): {next(gen)}")  # 0
print(f"  next(): {next(gen)}")  # 1
print(f"  next(): {next(gen)}")  # 4

print("\nExhausted - next() would raise StopIteration")

print("\n=== Unpacking ===")

# Unpack into variables
gen = (x for x in range(3))
a, b, c = gen
print(f"a, b, c = generator: {a}, {b}, {c}")

# Unpack with *
gen = (x ** 2 for x in range(5))
first, *rest = gen
print(f"first, *rest: first={first}, rest={rest}")

print("\n=== enumerate() with Generator ===")

words = ["apple", "banana", "cherry"]
gen = (w.upper() for w in words)

print("Enumerated generator:")
for i, word in enumerate(gen):
    print(f"  {i}: {word}")

print("\n=== zip() with Generators ===")

gen1 = (x for x in range(3))
gen2 = (x ** 2 for x in range(3))

print("Zipped generators:")
for a, b in zip(gen1, gen2):
    print(f"  {a} -> {b}")

Generators are single-use. Convert to list if you need to iterate multiple times.

Conditions in generator expressions

Filter values with if.

conditional.py
# Conditions in Generator Expressions

print("=== Filtering with if ===\n")

numbers = list(range(1, 11))
print(f"Numbers: {numbers}")

# Filter: only even numbers
evens = (n for n in numbers if n % 2 == 0)
print(f"Evens: {list(evens)}")

# Filter: only values > 5
big = (n for n in numbers if n > 5)
print(f"Greater than 5: {list(big)}")

print("\n=== Multiple Conditions ===")

# AND condition (both must be true)
even_and_big = (n for n in numbers if n % 2 == 0 and n > 5)
print(f"Even AND > 5: {list(even_and_big)}")

# Multiple if clauses (same as AND)
even_and_big2 = (n for n in numbers if n % 2 == 0 if n > 5)
print(f"Using multiple if: {list(even_and_big2)}")

print("\n=== Conditional Expression (if-else) ===")

# Transform based on condition
labels = ("even" if n % 2 == 0 else "odd" for n in range(1, 6))
print(f"Labels for 1-5: {list(labels)}")

# Different position!
print("""
Filter (if only):    (x for x in items if condition)
                                        ^^^^^^^^^^^^
                                        At the END

Transform (if-else): (a if cond else b for x in items)
                      ^^^^^^^^^^^^^^^^
                      At the BEGINNING
""")

print("=== Combining Filter and Transform ===")

data = list(range(-5, 6))
print(f"Data: {data}")

# Filter positives, then double them
result = (n * 2 for n in data if n > 0)
print(f"Positive numbers doubled: {list(result)}")

# Transform (absolute value) without filter
result2 = (abs(n) for n in data)
print(f"Absolute values: {list(result2)}")

# Conditional transform: double positive, negate negative
result3 = (n * 2 if n > 0 else -n for n in data if n != 0)
print(f"Transform (skip 0): {list(result3)}")

print("\n=== String Filtering ===")

words = ["apple", "ant", "Banana", "avocado", "Berry"]
print(f"Words: {words}")

# Lowercase words starting with 'a'
a_words = (w for w in words if w.lower().startswith('a'))
print(f"Start with 'a': {list(a_words)}")

# First letter, only long words
initials = (w[0] for w in words if len(w) > 5)
print(f"Initials of long words: {list(initials)}")

(x for x in items if x > 0) - filter while generating.

Exercise: practical.py

Real-world generator expression patterns