Python Generators with Examples


At its core, a Python generator is a special type of iterable, allowing the creation of iterators in a more elegant and memory-efficient manner. Unlike conventional list comprehensions or loops that generate and store all values at once, a generator produces values on-the-fly, as they are needed. This approach not only conserves memory resources but also accelerates code execution.

 

The Mechanics Behind Generators

 

Generators are defined using functions with the yield statement. When the function encounters the yield keyword, it doesn't execute the entire function immediately. Instead, it returns a generator object, ready to produce values one at a time. As each value is requested, the function resumes execution from where it left off until the next yield statement is encountered.

 

Here is a basic example of python generator :

def simple_generator():

    yield 1

    yield 2

    yield 3



gen = simple_generator()

print(next(gen))

print(next(gen))

print(next(gen))

Output

1
2
3

 

Generators are especially useful when dealing with large datasets where loading all the data into memory at once would be impractical. They are also commonly used to model infinite sequences, like the Fibonacci sequence:

def fibonacci_generator():

    a, b = 0, 1

    while True:

        yield a

        a, b = b, a + b



fib_gen = fibonacci_generator()

for i in range(10):

    print(next(fib_gen))

Output

0 1 1 2 3 5 8 13 21 34

 

fibonacci_generator() function: This function defines a generator that generates Fibonacci numbers. It uses the variables a and b to keep track of the last two Fibonacci numbers. Inside an infinite loop (while True), it uses the yield statement to yield the current value of a (the current Fibonacci number). Then, it updates the values of a and b to generate the next Fibonacci number.

 

 Prime Number Generator

def is_prime(n):

    if n <= 1:

        return False

    if n <= 3:

        return True

    if n % 2 == 0 or n % 3 == 0:

        return False

    i = 5

    while i * i <= n:

        if n % i == 0 or n % (i + 2) == 0:

            return False

        i += 6

    return True



def prime_generator():

    num = 2

    while True:

        if is_prime(num):

            yield num

        num += 1



# Input: Number of prime numbers to generate

num_primes = int(input("Enter the number of prime numbers to generate: "))



# Using the generator to print the specified number of prime numbers

prime_gen = prime_generator()

prime_list = []

for _ in range(num_primes):

    prime = next(prime_gen)

    prime_list.append(prime)



print(f"The first {num_primes} prime numbers are:")

print(prime_list)

Example input and output:

Enter the number of prime numbers to generate: 10

The first 10 prime numbers are:

[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

 

This example defines a more complex generator, prime_generator(), which generates prime numbers. The is_prime() function is used to check if a number is prime. The generator uses the yield statement to yield prime numbers as they are found, avoiding the need to store all prime numbers in memory.

 

is_prime(n) function: This function takes an integer n as input and checks whether it is a prime number. It follows an optimized primality checking algorithm. The initial conditions handle cases for small numbers. The loop iterates from 5 onwards in steps of 6 (since all primes greater than 3 can be written in the form 6k ± 1), checking divisibility to determine if the number is prime.

 

prime_generator() function: This function defines a generator that generates prime numbers. It starts from num = 2 and enters an infinite loop (while True). Inside the loop, it uses the is_prime() function to check if the current value of num is prime. If it is prime, the value is yielded using the yield statement. Then, the num variable is incremented for the next iteration.

 

Filtering With Generators

def even_numbers(start, end):

    current = start if start % 2 == 0 else start + 1

    while current <= end:

        yield current

        current += 2



# Using the generator to print even numbers between 1 and 20

even_gen = even_numbers(1, 20)

for num in even_gen:

    print(num)

Output

2
4
6
8
10
12
14
16
18
20

 

File Line Reader
def read_lines(file_path):

    with open(file_path, 'r') as file:

        for line in file:

            yield line.strip()



# Using the generator to read and print lines from a text file

file_path = 'sample.txt'

line_gen = read_lines(file_path)

for line in line_gen:

    print(line)

 

Advantages of Using Generators

 

1. Memory Efficiency

Generators are a game-changers when working with large datasets or streams of data. Traditional data structures like lists can quickly consume memory, causing performance bottlenecks. Generators, however, generate values lazily, ensuring that only one value is in memory at a time, regardless of the dataset's size.

 

2. Enhanced Performance

By producing values on the fly, generators significantly improve code execution speed. This is especially beneficial when dealing with time-consuming tasks like file I/O operations or complex calculations, where waiting for the entire dataset to be processed is impractical.

 

3. Simplified Implementation

Implementing generators in Python code is remarkably straightforward. By utilizing the yield statement, developers can create iterators without the need for intricate boilerplate code. This simplicity leads to cleaner, more readable codebases.

 

Seamless Integration of Generators

Integrating generators into your Python projects is a breeze. Start by defining a function with the yield statement, indicating where the values should be generated. Then, simply iterate through the generator using a loop or other iterable techniques, extracting values as needed.

 

Summary

 

Transitioning towards a more efficient and optimized codebase is a priority for developers across the globe. Python generators offer a clear path to achieving this goal. By harnessing the power of generators, programmers can create programs that consume fewer resources, run faster, and maintain a high level of readability.



Thanks for feedback.



Read More....
Arrays and Lists in Python
Python Iterators
Lambda or Anonymous Functions in Python
Most common built-in methods in Python
Python Decorators
Python Dictionaries