Difficulty: Advanced
Explain generators in Python. How are they different from regular functions and lists?
Generators are functions that use 'yield' to produce a sequence of values lazily (one at a time) instead of computing all values at once and returning a list.
Key differences from regular functions: - Use yield instead of return - Maintain their state between calls - Produce values on demand (lazy evaluation) - Memory efficient: generate items one at a time
A generator function returns a generator object that implements the iterator protocol (__iter__ and __next__).
import sys
# List: stores ALL values in memory
nums_list = [x 2 for x in range(1_000_000)]
print(f"List size: {sys.getsizeof(nums_list):,} bytes")
# Generator: computes values on demand
nums_gen = (x 2 for x in range(1_000_000))
print(f"Generator size: {sys.getsizeof(nums_gen):,} bytes")
# Generator function
def squares(n):
for i in range(n):
yield i 2 # pauses here, resumes on next()
for sq in squares(5):
print(sq, end=' ')
The list stores 1M values in memory (~8MB). The generator object is only 200 bytes because it computes values one at a time.
def countdown(n):
print("Starting countdown")
while n > 0:
yield n # pause, return n
n -= 1
print("Done!")
gen = countdown(3)
print(next(gen)) # Starting countdown, then 3
print(next(gen)) # 2
print(next(gen)) # 1
# next(gen) would print 'Done!' then raise StopIteration
# Practical: Reading large files line by line
def read_large_file(filepath):
with open(filepath) as f:
for line in f:
yield line.strip()
yield pauses the function and remembers its state. next() resumes from where it left off. This is perfect for processing data that doesn't fit in memory.
def read_lines(filepath):
with open(filepath) as f:
for line in f:
yield line.strip()
def filter_comments(lines):
for line in lines:
if not line.startswith('#'):
yield line
def parse_csv(lines):
for line in lines:
yield line.split(',')
def get_column(rows, col):
for row in rows:
yield row[col]
# Compose the pipeline - nothing executes until iteration
pipeline = get_column(
parse_csv(
filter_comments(
read_lines('data.csv')
)
),
col=0
)
for value in pipeline:
print(value)
Generator pipelines are composable and memory-efficient. Each stage processes one item at a time. The entire pipeline uses O(1) memory regardless of file size.
Generators, yield, Iterator Protocol, __iter__, __next__, Generator Expression