"""Flatten list of generators"""
from typing import Generator, Any
listofgenerators1: list[Generator[int, None, None]] = [
(_ for _ in range(10)),
(_ for _ in range(10, 20)),
]
print(listofgenerators1, type(listofgenerators1), type(listofgenerators1[0]))
listofgenerators2: list[Generator[int, None, None]] = [
(_ for _ in range(10)),
(_ for _ in range(10, 20)),
]
print(listofgenerators2, type(listofgenerators2), type(listofgenerators2[0]))
# Check if object is a generator with isinstance and types
import types
print(
type(listofgenerators1[0]), isinstance(listofgenerators1[0], (types.GeneratorType))
)
# or simply
print(
type(listofgenerators1[0]), str(type(listofgenerators1[0])) == "<class 'generator'>"
)
def combine_generators(listofgenerators) -> Generator[Any, Any, None]:
"""Yield from list of generators"""
for generator in listofgenerators:
yield from generator
# Flatten with yield from function
flattened_function: list[Any] | list[None] = list(combine_generators(listofgenerators1))
print(flattened_function, "Flattened with yield from function")
# Flatten with list comprehension
flattened_comprehension: list[Any] | list[None] = [
item for generator in listofgenerators2 for item in generator
]
print(flattened_comprehension, "Flattened with list comprehension")
'''Generator expressions are similar to list comprehensions,
but use parentheses instead of square brackets.
They generate items on-the-fly instead of creating a full list in memory.
'''
def numbers(start, end):
for i in range(start, end):
yield i
def combined():
yield from (numbers(start, start + 5) for start in range(1, 16, 5))
for item in combined():
print(item)
Flatten: TypeAlias = Callable[[list], list]
flatten: Flatten = lambda target: sum((flatten(sub)
if isinstance(sub, list)
else [sub] for sub in target), [])
# Example usage
nested_list: list[Any] = [1, [2, [3, 4], 5], ['a', 7], 8, [[[[[[5.3, 10], 11]]]]]]
print(flatten(nested_list))