PYTHON

Python Generator: Syntax, Usage, and Examples

Python generators allow you to create iterators in an efficient and memory-friendly way. Unlike regular functions, which return a single value and terminate, a generator can yield multiple values one at a time, suspending execution between each. This makes generators particularly useful for processing large data sets, streaming operations, and handling infinite sequences.

How to Use Python Generators

A generator function uses the yield keyword instead of return. This allows it to pause execution, return a value, and resume where it left off when called again.

# Basic generator function
def count_up_to(n):
    count = 1
    while count <= n:
        yield count  # Yield the current count value
        count += 1

# Using the generator
counter = count_up_to(5)
for num in counter:
    print(num)

Generator Expression

Python also allows you to create generators using a generator expression, similar to list comprehensions but with parentheses.

# Generator expression to create an infinite number sequence
squares = (x*x for x in range(1, 6))
print(list(squares))  # Output: [1, 4, 9, 16, 25]

When to Use Python Generators

Efficient Memory Usage

Generators process one item at a time instead of storing an entire sequence in memory. This reduces memory consumption, making them ideal for working with large files or data streams.

# Read a large file line by line
def read_large_file(file_path):
    with open(file_path, 'r') as file:
        for line in file:
            yield line.strip()

for line in read_large_file("big_data.txt"):
    print(line)

Lazy Evaluation for Performance

Generators compute values on demand instead of generating everything at once. This improves performance, especially when dealing with large datasets or computationally expensive calculations.

# Infinite number generator
def infinite_numbers():
    num = 0
    while True:
        yield num
        num += 1

number_gen = infinite_numbers()
print(next(number_gen))  # Output: 0
print(next(number_gen))  # Output: 1

Pipelining Data Processing

Generators allow you to process data in a pipeline, passing results from one generator to another.

# Chain multiple generators
def double_numbers(numbers):
    for num in numbers:
        yield num * 2

def filter_even(numbers):
    for num in numbers:
        if num % 2 == 0:
            yield num

numbers = range(10)
doubled = double_numbers(numbers)
even_numbers = filter_even(doubled)

print(list(even_numbers))  # Output: [0, 4, 8, 12, 16]

Examples of Python Generators

Creating a Generator for Fibonacci Numbers

Generators efficiently handle sequences such as the Fibonacci series without storing all numbers in memory.

def fibonacci_sequence(limit):
    a, b = 0, 1
    while a < limit:
        yield a
        a, b = b, a + b

fib = fibonacci_sequence(50)
print(list(fib))  # Output: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Using yield from for Nested Generators

The yield from statement simplifies working with nested generators by delegating to another iterable.

def sub_generator():
    yield 1
    yield 2
    yield 3

def main_generator():
    yield from sub_generator()
    yield 4

for value in main_generator():
    print(value)  # Output: 1, 2, 3, 4

Async Generators

Python’s async generators allow handling asynchronous data streams efficiently using async def and await.

import asyncio

async def async_counter():
    count = 0
    while count < 5:
        yield count
        count += 1
        await asyncio.sleep(1)

async def main():
    async for num in async_counter():
        print(num)

asyncio.run(main())

Learn More About Python Generators

Generator vs. Iterator

A generator is a type of [iterator]|(https://mimo.org/glossary/python/iterator) but requires less boilerplate code. An iterator implements __iter__() and __next__(), whereas a generator automatically supports these methods with yield.

# Custom iterator class
class CustomIterator:
    def __init__(self, limit):
        self.limit = limit
        self.count = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.count >= self.limit:
            raise StopIteration
        self.count += 1
        return self.count

# Using the custom iterator
custom_iter = CustomIterator(5)
for num in custom_iter:
    print(num)

Generators simplify this logic significantly.

Using send() in Generators

Generators can accept values using the send() method, which allows dynamic input between yields.

def interactive_generator():
    value = 0
    while True:
        received = yield value
        if received:
            value = received

gen = interactive_generator()
print(next(gen))  # Output: 0
print(gen.send(10))  # Output: 10

Handling Generator Exhaustion

Once a generator runs out of values, it raises a StopIteration exception. Handling this properly prevents errors.

gen = (x for x in range(3))
try:
    print(next(gen))  # Output: 0
    print(next(gen))  # Output: 1
    print(next(gen))  # Output: 2
    print(next(gen))  # Raises StopIteration
except StopIteration:
    print("Generator exhausted")

Python generators provide an elegant and powerful way to handle data streams efficiently. Whether you need to process large files, build an infinite sequence, or optimize memory usage, generators offer a flexible solution that simplifies code while improving performance.

Learn to Code in Python for Free
Start learning now
button icon
To advance beyond this tutorial and learn Python by doing, try the interactive experience of Mimo. Whether you're starting from scratch or brushing up your coding skills, Mimo helps you take your coding journey above and beyond.

Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.

You can code, too.

© 2025 Mimo GmbH