Type Hinting Generators in Python Using the typing Module

When working with generators, the typing module provides tools to define the types of the values they yield, accept, or return.

In this tutorial, you’ll learn how to use the Python typing module to annotate generators.

 

 

Basic Generator Type Hinting

You can specify a generator’s yield, send, and return types using Generator.

from typing import Generator
def employee_names() -> Generator[str, None, None]:
    names = ["Ahmed", "Salma", "Khaled"]
    for name in names:
        yield name

# Consume the generator
for employee in employee_names():
    print(employee)

Output:

Ahmed
Salma
Khaled

The generator yields strings, defined by Generator[str, None, None].

The send and return types are None because the generator doesn’t accept or return values.

When only the yield type matters, use Iterable instead of Generator.

from typing import Iterable
def employee_names() -> Iterable[str]:
    return (name for name in ["Ahmed", "Salma", "Khaled"])
for employee in employee_names():
    print(employee)

Output:

Ahmed
Salma
Khaled

Iterable[str] implies an iterator that produces strings without specifying send or return types.

 

Yield Type Annotations

You can annotate the type of values yielded by a generator.

from typing import Generator
def employee_salaries() -> Generator[int, None, None]:
    for salary in [5000, 7000, 6000]:
        yield salary
for salary in employee_salaries():
    print(salary)

Output:

5000
7000
6000

The generator yields integers representing employee salaries.

You can annotate with a union if a generator yields multiple types.

from typing import Generator, Union
def mixed_values() -> Generator[Union[int, str], None, None]:
    yield 42
    yield "Maha"
    yield 73
for value in mixed_values():
    print(value)

Output:

42
Maha
73

The Union[int, str] type accounts for the generator yielding both integers and strings.

 

Send Type Annotations

You can annotate the type accepted by send().

from typing import Generator
def feedback() -> Generator[str, int, None]:
    while True:
        received = yield "Waiting for feedback..."
        print(f"Received feedback score: {received}")
gen = feedback()
print(next(gen))
gen.send(8)

Output:

Waiting for feedback...
Received feedback score: 8

The send() method accepts integers, as specified by Generator[str, int, None].

 

Return Type Annotations

You can use Generator to indicate the return value after StopIteration.

from typing import Generator
def calculate_bonus() -> Generator[int, None, float]:
    yield 500
    yield 700
    return 100.0
gen = calculate_bonus()
for bonus in gen:
    print(bonus)
try:
    next(gen)
except StopIteration as e:
    print(f"Bonus calculation complete: {e.value}")

Output:

500
700
Bonus calculation complete: 100.0

The return value 100.0 is specified as the third argument of Generator.

 

Asynchronous Generators and Type Hinting

Async generators use AsyncGenerator for type hinting.

from typing import AsyncGenerator
async def async_employee_names() -> AsyncGenerator[str, None]:
    for name in ["Aya", "Nour", "Omar"]:
        yield name
import asyncio
async def main():
    async for name in async_employee_names():
        print(name)
asyncio.run(main())

Output:

Aya
Nour
Omar

Async generators yield values asynchronously, annotated with AsyncGenerator.

 

Generator Comprehensions and Type Hinting

You can use Generator for comprehensions.

from typing import Generator
numbers: Generator[int, None, None] = (x * 2 for x in range(3))
for num in numbers:
    print(num)

Output:

0
2
4

The generator comprehension is explicitly typed as Generator[int, None, None].

 

Recursive Generators and Type Hinting

Use recursion for generators producing nested data.

from typing import Generator
def nested_numbers(level: int) -> Generator[int, None, None]:
    if level == 0:
        yield 0
    else:
        yield level
        yield from nested_numbers(level - 1)
for num in nested_numbers(3):
    print(num)

Output:

3
2
1
0

The generator recursively yields integers by calling itself.

 

Type Hinting Infinite Generators

Annotate infinite generators to handle non-terminating cases.

from typing import Generator
def infinite_counter() -> Generator[int, None, None]:
    count = 0
    while True:
        yield count
        count += 1
counter = infinite_counter()
for _ in range(5):
    print(next(counter))

Output:

0
1
2
3
4

The infinite generator continuously yields integers, with no end condition.

Leave a Reply

Your email address will not be published. Required fields are marked *