Contents

Python Interactive Guide - Step 3 Functions (10) - Generator Functions

Series - Python Interactive Guide
Info
  • This course, Python Interactive Guide, is designed to help you learn the basics of Python programming through hands-on, interactive examples.
  • The “Style Guide” sections introduce clean coding practices, mainly based on PEP8.
  • You can run and experiment with every code example.
    Feel free to try things out - reloading the page will reset everything.

This is a continuation of “Step 3 Functions”.

In this section, we’ll learn about generator functions, special functions that handle data efficiently.

In “2.2.3. Comprehensions”, we covered generator expressions.
Generator expressions return generator objects with the following properties:

  • They are iterable (can be looped over)
  • They are “one-time use” objects that yield values in sequence
  • They generate values one at a time as needed, making them memory-efficient

Generator functions, which we’ll cover in this section, also return generator objects.

Basic Generator Function Syntax
def generator_name(): process yield value1 process yield value2 ...
  • A generator created by the function above executes code sequentially and returns the specified value when it reaches a yield statement. This process repeats until the function ends.
  • When the function ends, value generation also ends.
  • You can use the next() function to get values from the generator one at a time.
  • After all values are generated, calling next() raises a StopIteration exception.

As you can see from the examples above, generators produced by generator functions have the following characteristics:

  1. When next() is called, the code runs until it reaches the first yield statement and returns that value
  2. When next() is called again, execution resumes from immediately after the previous yield statement, runs until the next yield statement, and returns that value
  3. In both cases, if there is no next yield statement, the code runs to completion and then raises a StopIteration exception

In fact, a for loop works by “repeatedly calling next() and stopping when StopIteration is raised.”

📚 Exercise

Create a generator function called even_numbers that takes a parameter n and generates even numbers from 0 to n in sequence.

Sample Solution
📚 Exercise

Create a generator that produces the “Collatz sequence” which we also covered in the Recursive Functions section.

The Collatz sequence is a sequence of numbers that starts with a positive integer n and follows these rules until reaching 1:

  • If n is 1: Stop the process
  • If n is even: The next number is n / 2
  • If n is odd: The next number is 3n + 1

By repeating this procedure, it’s conjectured that any starting number will eventually reach 1 (the “Collatz Conjecture”).

(Example: For n=6, the sequence is 6 → 3 → 10 → 5 → 16 → 8 → 4 → 2 → 1)

Implement a generator collatz_generator that takes a parameter n and generates the Collatz sequence in order.

Sample Solution

Using generator functions, you can create infinite sequences that couldn’t be represented with a list.

When using an infinite generator in a for loop, you need to break the loop under some condition:

The yield from syntax allows you to yield values sequentially from an iterable (such as a list or generator).
This enables the creation of generators that combine multiple generators in a concise way:

yield from Syntax
yield from iterable

Without yield from, you would need verbose code like this:

yield from is not just a shorthand syntax—it also passes operations like send, throw, and close directly to the inner generator.

📚 Exercise

Create a generator function called combined_sequence using yield from that meets the following requirements:

  1. Take three different numerical lists as arguments
  2. Generate elements from each list in sequence
  3. Generate a separator value of -1 between each list
Sample Solution

So far, we’ve mainly used generators as “objects that produce values.” The following features allow you to send data to generators and control the flow of processing bidirectionally.

The send method allows you to send values to a generator.
The sent value is received as the return value of the yield expression:

Receiving values sent via the send method
variable = yield output_value

The above statement performs two operations:

  1. When it is reached, it outputs “output_value” and pauses execution
  2. When the send method is called while paused at this statement, its argument is assigned to “variable” and code execution continues until the next yield statement

To receive values using the send method, note the following:

  • To use the send method, the generator must be paused at a yield statement.
    Therefore, before using send for the first time, you need to advance to a yield statement using the next() function.
  • If the next() function is used while paused at the above statement, None is assigned to “variable.”
📚 Exercise

Create a function stats() that produces a “generator that displays statistics for received values.”

  • It receives numbers via the send method
  • Each time it receives a value, it displays the sum, count, and average of all values received so far
Sample Solution
📚 Exercise

Create a generator function error_monitor() that monitors logs.

  • It receives strings via the send method
  • If “ERROR” appears 3 times in a row, return “ALERT”
  • Otherwise, return None
Sample Solution

While the send method sends regular data, the throw method allows you to send exceptions to a generator.
When you call the throw method, the specified exception is raised at the position of the yield statement where the generator last paused.

By sending exceptions, you can control the execution flow of a generator.
This is useful for error handling and resource cleanup in generators.
(Error handling will be explained in detail later)

You can explicitly terminate a generator using the close method:

The close method is used when resources used by the generator need to be released.

In this section, we learned about generator functions.

  • Generator functions are special functions that use yield statements to generate data one item at a time

  • They are memory-efficient and can handle large or infinite data sequences effectively

  • Key features of generators:

    • They are “one-time use” objects that yield values in sequence
    • They use “lazy evaluation,” generating values one at a time as needed
    • Values can be extracted using for loops or the next() function
  • Advanced features:

    • yield from: A concise way to yield values from other iterables
    • send() method: Enables bidirectional communication by sending values to generators
    • throw() method: Sends exceptions to generators for flow control
    • close() method: Explicitly terminates a generator

Generator functions are powerful tools that can be used in various situations, such as processing large datasets and handling streams.
They’re especially useful when you need to handle large amounts of data while keeping memory usage low.

Related Content