Python Generators: Using yield to Create Generators
Introduction to Python Generators
Generator functions allow us to declare a function that behaves like an iterator, i.e. it can be used in a for loop. Instead of returning a single value like a regular function, a generator function yields a series of values, one at a time, pausing the function's state between each yield. This makes generators particularly useful for handling large datasets or streams of data efficiently.
In this tutorial, we'll explore how to create and use generators in Python with examples and, explanations.
Example 1: Basic Generator Function
A generator function is defined like a regular function but uses the ‘yield’ statement instead of 'return'.
This example demonstrates how to create a simple generator that yields numbers from 1 to 5. The 'yield' statement allows the function to return a value and pause its state, making it efficient for iteration.
Code:
Output:
1 2 3 4 5
Explanation:
- 'yield’ Statement:
- The 'yield' statement produces a value and pauses the function's execution, saving its state for the next time the generator is called.
- Using the Generator:
- The generator is used like an iterator. The 'for' loop automatically calls ‘next()’ on the generator, retrieving one value at a time.
Example 2: Generator for Fibonacci Sequence
Generators are well-suited for producing infinite or large sequences, such as the Fibonacci sequence.
This example shows how to create a generator for the Fibonacci sequence. The generator efficiently produces an infinite sequence, yielding one number at a time, without needing to store the entire sequence in memory.
Code:
Output:
0 1 4 9 16 25
Explanation:
- Generator Expression:
- The generator expression '(x * x for x in range(1, 6))' creates a generator that produces the squares of numbers from 1 to 5.
- Using the Generator:
- The 'for' loop iterates through the generator, retrieving each square and printing it.
Example 3: Controlling Generator Execution with send(), throw(), and close()
Generators in Python support additional methods like ‘send()’, ‘throw()’, and ‘close()’ to control their execution more precisely.
This example shows how to control a generator's execution using the 'send()', 'throw()', and 'close()' methods. The generator can accept values sent to it and perform cleanup when closed.
Code:
Output:
Received: Hello Received: World Generator closed
Explanation:
- 'send()' Method:
- The 'send()' method resumes the generator and sends a value to it, which is received at the point of the last yield.
- 'throw()' Method:
- (Not used in this example, but available) This method allows you to throw an exception inside the generator.
- 'close()' Method:
- The 'close()' method stops the generator by raising a ‘GeneratorExit’ exception inside it, which can be caught to perform cleanup.
Example 4: Generator for Reading Large Files
Generators are ideal for reading large files line by line, allowing you to process files that don't fit into memory.
This example demonstrates how to use a generator to read a large file line by line. This method is memory-efficient and suitable for processing large files that might not fit into memory.
Code:
Output:
1.000000000000000000e+00 2.000000000000000000e+00 3.000000000000000000e+00 4.000000000000000000e+00 5.000000000000000000e+00 6.000000000000000000e+00 7.000000000000000000e+00 8.000000000000000000e+00 9.000000000000000000e+00
Explanation:
- File Reading Generator ('read_large_file'):
- The generator reads a file line by line, yielding each line as it is read.
- This approach is memory-efficient because it doesn't load the entire file into memory.
Example 5: Chaining Generators
You can chain multiple generators together to create a more complex sequence.
This example demonstrates how to chain multiple generators together using yield from. The combined generator produces a sequence by iterating through each chained generator in order.
Code:
Output:
2 4 6 8 10 12 14 16 18 20
Explanation:
- Chaining Generators:
- The 'even_odd_chain' generator uses 'yield from' to chain the even and odd number generators.
- It first yields all values from the 'even_gen' and then continues with the 'odd_gen'.
Note:
- The yield from 'even_gen' statement causes the generator to yield all values from the ‘even_gen’ generator (which produces even numbers) until it is exhausted. However, the ‘even_gen’ generator is an infinite generator, meaning it never exhausts and keeps producing even numbers indefinitely.
- What Happens:
- When yield from 'even_gen' is called, the 'even_odd_chain' generator yields values from ‘even_gen’ (the even numbers) continuously.
- Since 'even_gen' never stops (it's an infinite loop that keeps yielding even numbers), the yield from ‘odd_gen’ part is never reached.
- As a result, the loop in 'even_odd_chain' never moves on to the odd numbers because it is stuck continuously yielding from the infinite 'even_gen'.