Introduction
Generators provide a memory-efficient way to create iterators using yield statements.
Creating Generators
# Generator function
def count_up_to(n):
count = 1
while count <= n:
yield count
count += 1
# Generator expression
squares = (x**2 for x in range(1000000))
# Using generator
for num in count_up_to(5):
print(num)
Why Use Generators?
# Memory comparison
import sys
# List (stores everything in memory)
numbers_list = [x**2 for x in range(10000)]
print(sys.getsizeof(numbers_list)) # ~80KB
# Generator (computes on demand)
numbers_gen = (x**2 for x in range(10000))
print(sys.getsizeof(numbers_gen)) # ~200 bytes
Generator Methods
def infinite_counter():
count = 0
while True:
yield count
count += 1
counter = infinite_counter()
next(counter) # 0
next(counter) # 1
print(counter.send(10)) # 11 (send value to yield)
counter.close() # Stop generator
Practice Problems
- Create Fibonacci generator
- Generate prime numbers indefinitely
- Read large file line by line
- Implement lazy evaluation of expressions
- Use send() to control generator flow