A Beginner's Guide to Understanding Python Generator Functions
In Python, yield is a keyword that is used in conjunction with generator functions to create iterators. When a function is defined using yield, it becomes a generator function.
The generator function, instead of returning a value, yields a series of values, one at a time, using the yield keyword. Each time a value is yielded, the state of the function is saved, and the function is paused until the next value is requested. That means we can directly loop the generator function until it stops generating results, or stop yielding.
Here is an example of a generator function that yields a series of numbers:
def countdown(n):
? ? while n > 0:
? ? ? ? yield n
? ? ? ? n -= 1
When this function is called with an integer argument, it returns a generator object:
>>> c = countdown(5)
>>> c
<generator object countdown at 0x7f94435ad4c0>)
To get the next value in the series, you can call the next() method on the generator object:
>>> next(c
5
>>> next(c)
4
>>> next(c)
3
The generator function keeps yielding values until it has nothing left to yield, at which point it raises a StopIteration exception.
Generator functions are useful when we need to generate a large number of values, but we don't want to generate them all at once and store them in memory. Instead, we can generate them on-the-fly, as they are needed. This can be much more memory-efficient than creating a large list or other data structure.
Here are some real-world examples of how Python's yield can be used:
Overall, yield is a powerful feature in Python that can be used in a wide range of applications, from processing large files to implementing complex programming patterns.
Basic code examples of how Python's yield can be used.
def number_sequence(n):
? ? for i in range(1, n+1):
? ? ? ? yield i
# Using the generator function
for num in number_sequence(5):
? ? print(num)
Output:
1
2
3
4
5
2. Reading a large file line-by-line: The following code defines a generator function that reads a large file line-by-line:
def read_file(filename):
? ? with open(filename) as file:
? ? ? ? for line in file:
? ? ? ? ? ? yield line.strip()
# Using the generator function
for line in read_file('large_file.txt'):
? ? process_line(line)
3. Iterating over a database: The following code defines a generator function that iterates over a database, returning one record at a time:
def get_records():
? ? conn = sqlite3.connect('mydatabase.db')
? ? cursor = conn.cursor()
? ? cursor.execute('SELECT * FROM mytable')
? ? for row in cursor.fetchall():
? ? ? ? yield row
# Using the generator function
for record in get_records():
? ? process_record(record)
4. Implementing a simple coroutine: The following code defines a simple coroutine that prints a message, waits for a second, and then prints another message:
def coroutine():
? ? print('Starting coroutine')
? ? yield
? ? time.sleep(1)
? ? print('Resuming coroutine')
? ? yield
? ? print('Coroutine finished')
# Using the coroutine
c = coroutine()
next(c) # Starting coroutine
next(c) # Resuming coroutine
next(c) # Coroutine finished
5. Implementing a state machine: The following code defines a generator function that implements a simple state machine with two states:
def state_machine():
? ? state = 0
? ? while True:
? ? ? ? if state == 0:
? ? ? ? ? ? print('State 0')
? ? ? ? ? ? state = yield 'State 0'
? ? ? ? elif state == 1:
? ? ? ? ? ? print('State 1')
? ? ? ? ? ? state = yield 'State 1'
# Using the state machine
s = state_machine()
next(s) # State 0
s.send(1) # State 1
s.send(0) # State 0
These are just a few basic examples of how yield can be used in Python. The possibilities are endless!