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.
Mokhtar is the founder of LikeGeeks.com. He is a seasoned technologist and accomplished author, with expertise in Linux system administration and Python development. Since 2010, Mokhtar has built an impressive career, transitioning from system administration to Python development in 2015. His work spans large corporations to freelance clients around the globe. Alongside his technical work, Mokhtar has authored some insightful books in his field. Known for his innovative solutions, meticulous attention to detail, and high-quality work, Mokhtar continually seeks new challenges within the dynamic field of technology.