Python Generators: Yielding and Iterators – PL Courses

Python Generators: Yielding and Iterators

Generators and iterators are important concepts in Python programming. They provide efficient ways to work with large sets of data or process data lazily. In this tutorial, we will explore generators, how to create them using the yield keyword, and how to work with iterators.


In Python, a generator is a special type of function that returns an iterable generator object. The generator object can be used to lazily generate a sequence of values rather than computing them all at the same time. This is particularly useful when working with large datasets and helps in optimizing memory usage.

Generators are defined using the yield keyword. Let’s see an example:

def count_down(n):
    while n > 0:
        yield n
        n -= 1

# Create a generator object
my_generator = count_down(5)

# Print the values
for num in my_generator:

In the example above, we defined a generator function called count_down. It takes an integer n and yields each number in reverse order from n down to 1. When the yield statement is encountered, the function pauses execution and returns the yielded value. The next time the function is called, it resumes from where it left off.

We created a generator object by calling the count_down function with an initial value of 5. The generator object can then be iterated over using a for loop. Each time the loop iterates, it prints the yielded value.

Using generators helps in saving memory as it generates values on-the-fly, as needed. This is in contrast to creating a list containing all values upfront, which can quickly consume memory for large datasets.

Iterator Protocol

An iterator is an object that implements the iterator protocol, which requires two methods: __iter__() and __next__(). The __iter__() method returns the iterator object itself, while the __next__() method returns the next value in the sequence or raises a StopIteration exception if there are no more elements.

Generators automatically implement the iterator protocol, making it simple to iterate over their values. Let’s see an example:

def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b
# Create a generator object
fib_generator = fibonacci()

# Print the Fibonacci sequence
for num in fib_generator:
    if num > 1000:

In this example, we defined a generator function called fibonacci. It generates the Fibonacci sequence indefinitely. We created a generator object using this function and then used a for loop to iterate over the values. We stop the iteration when the value exceeds 1000. Since the generator functions implements the iterator protocol, we can directly use it in the for loop.

Advantages of Generators

Generators offer several advantages over other approaches:

  • Generators produce only one value at a time, saving memory resources compared to creating and storing a complete sequence.
  • Values are generated on-demand, which is useful when working with large datasets where not all values are needed simultaneously.
  • Generators enable the generation of sequences that would be impossible to store in memory, such as an infinite series or a continuously updated stream of data.

By using generators, we can write more efficient and scalable Python code that can handle large datasets without running out of memory.

In this tutorial, we covered the concept of generators in Python. We learned how to create generators using the yield keyword and how they can be used to lazily generate sequences of values. Additionally, we explored how generators automatically implement the iterator protocol, making them easy to work with in for loops and other iterable contexts. Generators provide memory efficiency, lazy evaluation, and the ability to work with potentially infinite sequences, making them an essential tool for any Python programmer.


You might also like this video