Generator Functions

Introduction

A Generator Function is a special type of function that uses the yield keyword instead of return to produce values one at a time.

Unlike normal functions that return all results at once and then terminate, generator functions pause execution after each yield and resume when the next value is requested.

Generator functions are widely used in:

  • Large Data Processing

  • File Handling

  • Log Analysis

  • API Pagination

  • Data Streaming

  • Automation Frameworks

  • Memory-Efficient Applications

In this tutorial, you will learn what generator functions are, how they work, practical examples, automation testing use cases, common mistakes, and best practices.


What is a Generator Function?

A generator function is a function that contains one or more yield statements.

When called, it does not execute immediately. Instead, it returns a generator object that can generate values one by one.

Syntax

def generator_function():
    yield value

Why Use Generator Functions?

Generator functions help:

  • Save memory

  • Process large datasets efficiently

  • Generate values on demand

  • Improve performance

  • Support lazy evaluation


Normal Function vs Generator Function

Normal Function

def numbers():
    return [1, 2, 3]

print(numbers())

Output

[1, 2, 3]

Returns all values at once.


Generator Function

def numbers():
    yield 1
    yield 2
    yield 3

print(numbers())

Output

<generator object numbers at 0x...>

Returns a generator object.


Basic Generator Function Example

Example

def numbers():
    yield 1
    yield 2
    yield 3

for number in numbers():
    print(number)

Output

1
2
3

Values are generated one at a time.


Understanding How Generators Work

Example

def demo():

    print("Start")
    yield 1

    print("Middle")
    yield 2

    print("End")
    yield 3

generator = demo()

print(next(generator))
print(next(generator))
print(next(generator))

Output

Start
1
Middle
2
End
3

The function pauses at each yield.


Using next() with Generators

Example

def count():

    yield 10
    yield 20
    yield 30

generator = count()

print(next(generator))
print(next(generator))
print(next(generator))

Output

10
20
30

next() retrieves the next generated value.


Generator Function with Loop

Example

def generate_numbers():

    for i in range(5):
        yield i

for number in generate_numbers():
    print(number)

Output

0
1
2
3
4

Generator Function with Conditions

Example

def even_numbers(limit):

    for num in range(limit):

        if num % 2 == 0:
            yield num

for number in even_numbers(10):
    print(number)

Output

0
2
4
6
8

Generator Function Returning Squares

Example

def squares(limit):

    for num in range(limit):
        yield num ** 2

for value in squares(5):
    print(value)

Output

0
1
4
9
16

Infinite Generator Function

Example

def infinite_counter():

    number = 1

    while True:
        yield number
        number += 1

generator = infinite_counter()

print(next(generator))
print(next(generator))
print(next(generator))

Output

1
2
3

Generators can produce unlimited values.


Memory Efficiency of Generators

Using List

numbers = [x for x in range(1000000)]

Creates all values in memory.


Using Generator

numbers = (
    x for x in range(1000000)
)

Generates values only when needed.

This saves memory.


Generator Function Reading Files

Example

def read_file(file_name):

    with open(file_name) as file:

        for line in file:
            yield line

for line in read_file("data.txt"):
    print(line)

Only one line is loaded at a time.


Generator Function Processing Logs

Example

def log_reader():

    logs = [
        "INFO",
        "WARNING",
        "ERROR"
    ]

    for log in logs:
        yield log

for entry in log_reader():
    print(entry)

Output

INFO
WARNING
ERROR

Generator Function for API Pagination

Example

def api_pages():

    yield "Page 1"
    yield "Page 2"
    yield "Page 3"

for page in api_pages():
    print(page)

Output

Page 1
Page 2
Page 3

Useful when APIs return paginated data.


yield from in Generator Functions

Used to delegate iteration to another generator.

Example

def numbers():

    yield 1
    yield 2

def values():

    yield from numbers()
    yield 3

for item in values():
    print(item)

Output

1
2
3

Generator Functions in Selenium Automation

Example

Generate multiple test users.

def test_users():

    yield "admin"
    yield "manager"
    yield "tester"

for user in test_users():
    print(user)

Output

admin
manager
tester

Useful for data-driven testing.


Generator Functions in API Automation

Example

def status_codes():

    yield 200
    yield 201
    yield 404

for code in status_codes():
    print(code)

Output

200
201
404

Useful for validating multiple responses.


StopIteration Exception

When a generator has no more values, Python raises StopIteration.

Example

def numbers():
    yield 1

generator = numbers()

print(next(generator))
print(next(generator))

Output

1

StopIteration

Common Mistakes Beginners Make

Using return Instead of yield

Incorrect

def numbers():
    return 1
    return 2

Only the first return executes.


Correct

def numbers():
    yield 1
    yield 2

Forgetting to Iterate

Incorrect

generator = numbers()

Produces no output.


Correct

for value in generator:
    print(value)

Calling next() Excessively

Example

generator = numbers()

next(generator)
next(generator)
next(generator)

May raise:

StopIteration

Expecting a List

Example

print(numbers())

Output

<generator object ...>

Convert if necessary:

list(numbers())

Best Practices

Use Generators for Large Data

Ideal for:

  • Files

  • Logs

  • Database Records

  • API Responses


Keep Generator Functions Focused

One generator should handle one responsibility.


Use Meaningful Names

Examples:

read_logs()
generate_users()
fetch_pages()

Use yield from for Nested Generators

yield from another_generator()

Improves readability.


Prefer Loops Over Repeated next()

Safer and cleaner.


Advantages of Generator Functions

  • Memory efficient

  • Faster for large datasets

  • Lazy evaluation

  • Cleaner code

  • Better performance


Limitations of Generator Functions

  • Values can be consumed only once

  • No random access

  • Slightly harder to debug


Generator Function vs Normal Function

Feature Generator Function Normal Function
Uses yield Yes No
Uses return Optional Yes
Returns Generator Object Yes No
Memory Efficient Yes No
Lazy Evaluation Yes No
Produces Values One by One Yes No

Conclusion

Generator functions are one of Python’s most powerful features for creating memory-efficient and scalable applications. By using the yield keyword, they generate values on demand rather than storing everything in memory.

Whether you’re processing large files, handling API pagination, streaming data, or building Selenium automation frameworks, generator functions can significantly improve performance and reduce memory usage.

Mastering generator functions is an important step toward advanced Python programming and writing production-ready applications.


Frequently Asked Questions (FAQs)

What is a generator function?

A generator function is a function that uses yield to generate values one at a time.


What does a generator function return?

It returns a generator object.

gen = my_generator()

How is a generator different from a normal function?

A normal function returns all results at once, while a generator produces values one at a time.


Why are generators memory efficient?

Because they generate values only when needed instead of storing all values in memory.


What is yield from?

It delegates iteration to another generator.

yield from another_generator()

Key Takeaways

  • Generator functions use the yield keyword.

  • They return generator objects.

  • Values are generated one at a time.

  • Generators support lazy evaluation.

  • They are memory efficient.

  • next() retrieves the next generated value.

  • yield from delegates to another generator.

  • Useful for large files, logs, and API pagination.

  • Commonly used in Selenium and API automation frameworks.

  • Generator functions help build scalable and high-performance Python applications.