Lesson 2: Generators

Generators are a powerful feature in Python that allow you to create iterators in a more memory-efficient and readable way. Instead of returning all values at once like regular functions, generators produce items one at a time using the yield keyword. This makes them especially useful when dealing with large datasets or infinite sequences.

In this lesson, we’ll cover:

  1. Using yieldto Create Generators
  2. Differences Between Generators and Functions

1. Using yield to Create Generators

What is a Generator?

A generator is a special type of function that returns an iterator. Instead of using the return statement, a generator uses the yield keyword to produce a value and pause the function’s state. This means the function can be resumed later from where it left off.

Why Use Generators?

  • Memory Efficient:They don’t store the entire sequence in memory.
  • Lazy Evaluation:Values are generated only when needed.
  • Readable Code:Cleaner syntax compared to managing state manually.

Creating a Simple Generator:

python
def simple_generator():

yield 1

yield 2

yield 3

 

# Using the generator

for value in simple_generator():

print(value)

 

Explanation:

  • The function simple_generatoryields three values: 1, 2, and 3.
  • Each time yieldis encountered, the function pauses, saving its current state.
  • The next iteration resumes from where it left off.

Generator with Loop:

python
def count_up_to(n):

count = 1

while count <= n:

yield count

count += 1

 

for num in count_up_to(5):

print(num)

 

Key Points:

  • The generator count_up_toyields numbers from 1 to n.
  • No list is created in memory, making it efficient for large ranges.

2. Differences Between Generators and Functions

Aspect Regular Functions Generators
Keyword Used Uses return to send back a value. Uses yield to produce values one at a time.
Memory Usage Stores entire data in memory. Generates values on the fly (memory efficient).
Execution Runs to completion when called. Pauses at yield and resumes later.
Return Value Returns a single value or data structure. Returns a generator object (iterator).
State Retention Does not retain state between calls. Retains state between yield calls.

Example: Function vs. Generator

  • Regular Function:
python
def get_numbers():

return [1, 2, 3, 4, 5]

 

for num in get_numbers():

print(num)

 

  • Generator:
python
def generate_numbers():

for i in range(1, 6):

yield i

 

for num in generate_numbers():

print(num)

 

Memory Usage:

  • The function get_numbers()creates and stores the entire list in memory.
  • The generator generate_numbers()yields one number at a time, reducing memory usage.

Real-World Use Cases for Generators:

  1. Large Data Processing:Reading large files without loading the entire file into memory.
  2. Streaming Data:Processing data streams like logs, API responses, or real-time feeds.
  3. Infinite Sequences:Generating endless series (e.g., Fibonacci numbers) without memory overflow.

Example – Reading Large Files Efficiently:

python
def read_large_file(file_path):

with open(file_path, ‘r’) as file:

for line in file:

yield line.strip()

 

for line in read_large_file(‘bigdata.txt’):

print(line)

 

Why It’s Efficient:

  • Only one line is loaded into memory at a time, making it scalable for large datasets.

Summary

  • Generatorsallow you to write memory-efficient code using the yield
  • They differ from regular functions by pausing and resuming execution without losing state.
  • Generators are ideal for large data processing, streaming applications, and infinite sequences.

In the next lesson, we’ll explore Context Managers to handle resource management more effectively.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *